Home Page
  • March 30, 2025, 11:48:36 pm *
  • Welcome, Guest
Please login or register.

Login with username, password and session length
Advanced search  

News:

Official site launch very soon, hurrah!


Pages: [1] 2 3 ... 10
 1 
 on: March 11, 2025, 06:09:06 pm 
Started by Dakusan - Last post by Dakusan
Original post for Goodbye Pidgin can be found at https://www.castledragmire.com/Posts/Goodbye_Pidgin.
Originally posted on: 03/11/25

You served me well for 15 years.

 2 
 on: February 17, 2025, 11:47:54 pm 
Started by Dakusan - Last post by Dakusan
My script actually just caught a change, and fortunately one that I am very happy with. (I had of course already removed the "home" file permission when I first installed it).

Code: [Select]
io.github.ungoogled_software.ungoogled_chromium: Permission changes:
  [Context]
    + filesystems = xdg-run/pipewire-0;~/.local/share/icons:create;xdg-download;xdg-desktop;~/.local/share/applications:create
    - filesystems = home
  [Environment]
    - LD_LIBRARY_PATH = /app/chromium/nonfree-codecs/lib
  [Extension io.github.ungoogled_software.ungoogled_chromium.Codecs]
    - directory = chromium/nonfree-codecs
    - autodelete = true
    - add-ld-path = lib
  [Build]
    - built-extensions = io.github.ungoogled_software.ungoogled_chromium.Codecs

 3 
 on: December 20, 2024, 01:29:29 pm 
Started by Dakusan - Last post by Dakusan

I finally got my Raz and Lili plushies in from the Psychonauts 2 campaign, NINE YEARS LATER. They’re pretty cute and I really like them, though Lilis’ has a lot of flaws. Her blouse colors are way off and the triangle pattern is much larger than in the game. Her skirt is also only 1 color instead of 2. All in all not that big a deal.

Something that really urks me about it though is that they were supposed to be CAMPAIGN EXCLUSIVES that I paid $100 for each. But then Double Fine went and sold them online for $40 a pop.


They already lost my good faith from the “Backpack” campaign reward debacle  4 years ago. It wasn’t the backpack from the first game with the badges. It was the new satchel design from the second game, which is NOT what was promised. I paid $300 bucks for it and its definitely supposed to be the bag from #1 if its supposed to include the merit badges... Granted, the quality was very nice though.


 4 
 on: December 20, 2024, 11:30:22 am 
Started by Dakusan - Last post by Dakusan

I updated the Checking permissions before updating Flatpaks post with the following changes:

  • Split post into the following sections:
    • My final updated copy of the script
    • List of the changes I made from the AI produced script
    • The commands I gave the AI
    • The final script the AI produced
  • Made more updates to the final script:
    • Added “askQuestion” function since there are now multiple places in the script that ask questions
    • Added $NoNewline to outputColor (for “askQuestion” function)
    • Changed get-updates command from “flatpak remote-ls --updates” to “echo n | flatpak update” (with a regex extraction). Also now confirms the update list with the user.

 5 
 on: December 07, 2024, 02:25:44 am 
Started by Dakusan - Last post by Dakusan

Listening to music when working to drown out everything but what I’m concentrating on has long been the way I’ve done things, and sound quality is important to me.


When I’ve had housemates, I always used my Sennheiser HD 650 Open Back headphones along with an RME Babyface Pro for custom wave tuning. To perfectly tune the waveforms so it sounded the best to me, I spent many hours listening to the same sound clips over and over, ones that I’d already heard thousands of times before and knew impeccably.


Now that I’ve had my house to myself for a while, I always listen to everything through surround sound (I have my house wired for 7.1 in my office, living room, and bedroom). While the sound quality is a pretty big step down, I really enjoy being immersed in the music, coming in from all directions. Any headphones I’ve tried just can’t simulate that experience.


I generally just listen to stereo tracks that are upmixed (upmuxed?) to 7.1, which gives me what I want. Actual surround sound music tracks are rare, and it’s even more rare that they are actually good.


In 1996 there were 5 Eva (Neon Genesis Evangelion) OST CDs released, and in December of 2004 they were rereleased with 5.1 surround editions. I have acquired copies of the first 3 of these CDs and the surround mixes are phenomenal. I’ve been saddened by the fact I don’t have the 5th CD (I’ve found a physical copy online I intend to grab soon) as it contains one of my favorite Eva songs, “Komm, süsser Tod”.


It recently came to my attention that there was a “Neon Genesis Evangelion 5.1ch Surround Edition Soundtrack” released in 2015 that has that song and I finally acquired it today. Unfortunately, the surround remix on this CD is horrible and I deleted it after listening all the way through. Very disappointing.


Another fun tidbit. I’ve lost a good deal of my hearing ranges from playing percussion in instrumental band in high school. I recently went to an audiologist and tried out hearing aids and HOLY CRAP, everything in life suddenly sounds so much better and crisper. The Oticon Intent hearing aids are absolutely amazing. The best versions (#1) definitely give the best quality. The lowest tier version (#3) didn’t cut it for me. I’m probably going to settle with the mid-tier version (#2) due to price vs quality loss.


Using these hearing aids is a huge step for me in bridging the gap to listening to music out loud and through my Sennheisers. And what really surprised me was that using the hearing aids actually enhanced the sound quality when listening through my Sennheisers.


To adjust the hearing aids to my needs I went through a 10-ish minute test in a sound booth in which all the ranges of my hearing were testing, so the hearing aids could boost the different ranges to match my hearing loss. What really made me smile was when I compared the hearing range loss chart with the waveform I came up with for my Babyface. They were almost exactly matched. Which tells me I haven’t lost much more hearing in the last decade.


 6 
 on: November 26, 2024, 05:00:54 pm 
Started by Dakusan - Last post by Dakusan
One other note I wanted to add. Trying to LUKS encrypt your boot partition comes with its own caveats. Grub bootloader decryption (before initramfs loads) is SLOW. Like... 20 seconds to test a password on modern hardware due to iteration counts and low level bootloaders not having access to certain CPU functions that would speed up the process. Also, if you mistype the password, you may have to reboot.

 7 
 on: November 21, 2024, 11:39:33 pm 
Started by Dakusan - Last post by Dakusan
Original post for Sekiro letter opener sword can be found at https://www.castledragmire.com/Posts/Sekiro_letter_opener_sword.
Originally posted on: 11/22/24

In honor of my recent Sekiro accomplishment, I bought myself a cute little Sekiro letter opener sword to go on my office side desk.


I honestly think I may consider Sekiro the best first player game ever made. It’s by far better than any of From Softwares’ other games (I am a big Dark Souls fan. Elden ring was boringly easy). I had been trying to do a perfect run for about a month and finally got it done.


In 10.5 hours I killed every enemy exactly once (no more, no less), 0 death screens, no cheese, no exiting battles, bought out all vendors, and collected all items. It was a lot of fun.


One of the reasons I love Sekiro so much is that it’s about the zen perfection of a specific skill set, patience, timing, and reaction speed (I am also a big fan of Super Hexagon - almost have a 4 minute run on it). There is no experience grinding to get past bosses, though certain skills acquired through exp are extremely helpful. This makes it harder than dark souls, and winning gives much more of an accomplishment feeling.


It goes far beyond that though. The level design is top notch and art assets are visually stunning. It’s an integrated cohesive world more so than any of the Dark Souls, and I’m a bit of a sucker for Japanese themes.


Combat is wonderful. Pretty much all the fights are fun and fair once you learn how to play, and you can even freaking pause the game! :-D You have to keep on your toes at all times cause ANYONE can kill you. The game both lets you sneak or just run in and murder everything if you’re good enough. The replayability is much higher than any of the other FromSoft souls like games too.



 8 
 on: November 20, 2024, 10:12:14 pm 
Started by Dakusan - Last post by Dakusan

I recently moved to Linux and have all my hard drives Luks encrypted, including the primary. I decided to convert my ext4 partitions to Btrfs recently, which I’m totally loving. I also decided to grab another nvme drive and use it as a RAID1 (mirror) drive against my primary drive, using Btrfs’ RAID mechanics. Below are the instructions to accomplish this.

Do note that this is for a situation where you already have a BTRFS volume and want to add a device as RAID1. This assumes you already have your system booting to the LUKS encrypted drive with the root being btrfs. Many modern Linux OS installers can do this for you automatically. Parts of these instructions can still be used in other situations.


  • Hopefully you also have a swap partition under the same LVM as your LUKS root (the Linux Mint installer does this by default), as we’ll be using it. If not, you’ll need to modify the instructions. This script resizes the swap partition and adds an “extra” partition to hold your drive key. This is required because a drive key cannot be loaded off your btrfs volume as both drives need to be unlocked first.
  • This should be ran from another operating system. I would recommend using Universal USB Installer to do this. It allows you to put multiple OS live cds on a USB key, including optional persistence.
  • Run the following script as root (you can use sudo). Make sure to fill in the variables section first. Or even better, run the script 1 line at a time to make sure there are no problems.


#!/bin/bash
#-----------------------------------Variables----------------------------------
#Current root drive
CurPart="nvme0n1p3" #The current drive partition in /dev. This example uses nvme disk #0 partition #3
CurCryptVol="vgmint" #What you named your LVM under LUKS
CurCryptRoot="root" #What you named your root partition under the LVM
CurCryptRootSubVol="/" #The path of the subvolume that is used as the root partition. For example, I use “@”
CurCryptSwap="swap_1" #What you named your swap partition under the LVM
CurCryptExtra="extra" #What you WANT to name your extra partition under the LVM
CurCryptExtraSize="100M" #How big you want your extra partition that will hold your key file
CurKeyPath="" #The path to a key file that will unlock both drives. If left blank then one will be created

#New drive
NewDrive="nvme1n1" #The new drive in /dev. This example uses nvme disk #1
NewPart="nvme1n1p3" #The new partition in /dev. You will be creating this with the cfdisk. This example uses nvme disk#1 partition#3
NewCryptName="raid1_crypt" #What we’ll name the root LUKS partition (no LVM)

#Other variables you do not need to set
CurMount="/mnt/primary"
ExtraMountPath="$CurMount/mnt/extra"
BtrfsReleasePath="kdave/btrfs-progs"
BtrfsReleaseFile="btrfs.box.static"
DriveKeyName="drivekey"

echo "---------------------------------Update BTRFS---------------------------------"
echo "Make sure you are using the latest btrfs-progs"
cd "$(dirname "$(which btrfs)")"
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/$BtrfsReleasePath/releases/latest" | grep tag_name | cut -d \" -f4)
wget "https://github.com/$BtrfsReleasePath/releases/download/$LATEST_RELEASE/$BtrfsReleaseFile"
chmod +x "$BtrfsReleaseFile"

echo "Link all btrfs programs to btrfs.box.static. Rename old files as .old.FILENAME"
if ! [ -L ./btrfs ]; then
 for v in $(\ls btrfs*); do
   if [ "$v" != "$BtrfsReleaseFile" ]; then
     mv "$v" ".old.$v"
     ln -s "$BtrfsReleaseFile" "$v"
   fi
 done
fi

echo "--------------------------Current drive and key setup-------------------------"
echo "Mount the current root partition"
cryptsetup luksOpen "/dev/$CurPart" "$CurCryptVol"
vgchange -ay "$CurCryptVol"
mkdir -p "$CurMount"
mount -o "subvol=$CurCryptRootSubVol" "/dev/$CurCryptVol/$CurCryptRoot" "$CurMount"

echo "If the extra volume has not been created, then resize the swap and create it"
if ! [ -e "/dev/$CurCryptVol/$CurCryptExtra" ]; then
 lvremove -y "/dev/$CurCryptVol/$CurCryptSwap"

 lvcreate -n "$CurCryptExtra" -L "$CurCryptExtraSize" "$CurCryptVol"
 mkfs.ext4 "/dev/$CurCryptVol/$CurCryptExtra"

 lvcreate -n "$CurCryptSwap" -l 100%FREE "$CurCryptVol"
 mkswap "/dev/$CurCryptVol/$CurCryptSwap"
fi

echo "Make sure the key file exists, if it does not, either copy it (if given in $CurKeyPath) or create it"
mkdir -p "$ExtraMountPath"
mount "/dev/$CurCryptVol/$CurCryptExtra" "$ExtraMountPath"
if ! [ -e "$ExtraMountPath/$DriveKeyName" ]; then
 if [ "$CurKeyPath" != "" ]; then
   if ! [ -e "$CurKeyPath" ]; then
     echo "Not found: $CurKeyPath"
     exit 1
   fi
   cp "$CurKeyPath" "$ExtraMountPath/$DriveKeyName"
 else
   openssl rand -out "$ExtraMountPath/$DriveKeyName" 512
 fi
 chmod 400 "$ExtraMountPath/$DriveKeyName"
 chown root:root "$ExtraMountPath/$DriveKeyName"
fi

echo "Make sure the key file works on the current drive"
if cryptsetup --test-passphrase luksOpen --key-file "$ExtraMountPath/$DriveKeyName" "/dev/$CurPart" test; then
 echo "Keyfile successfully opened the LUKS partition."
 #cryptsetup luksClose test #This doesn’t seem to be needed
else
 echo "Adding keyfile to the LUKS partition"
 cryptsetup luksAddKey "/dev/$CurPart" "$ExtraMountPath/$DriveKeyName"
fi

echo "--------------------------------New drive setup-------------------------------"
echo "Use cfdisk to set the new disk as GPT and add partitions."
echo "Make sure to mark the partition you want to use for the raid disk as type “Linux Filesystem”."
echo "Also make it the same size as /dev/$CurPart to avoid errors"
cfdisk "/dev/$NewDrive"

echo "Encrypt the new partition"
cryptsetup luksFormat "/dev/$NewPart"

echo "Open the encrypted partition"
cryptsetup luksOpen "/dev/$NewPart" "$NewCryptName"

echo "Add the key to the partition"
cryptsetup luksAddKey "/dev/$NewPart" "$ExtraMountPath/$DriveKeyName"

echo "Add the new partition to the root btrfs file system"
btrfs device add "/dev/mapper/$NewCryptName" "$CurMount"

echo "Convert to RAID1"
btrfs balance start -dconvert=raid1 -mconvert=raid1 "$CurMount"

echo "Confirm both disks are in use"
btrfs filesystem usage "$CurMount"

echo "--------------------Booting script to load encrypted drives-------------------"
echo "Get the UUID of the second btrfs volume"
Drive2_UUID=$(lsblk -o UUID -d "/dev/$NewPart" | tail -n1)

echo "Create a script to open your second luks volumes before mounting the partition"
echo "Note: In some scenarios this may need to go into “scripts/local-premount” instead of “scripts/local-bottom”"
cat <<EOF > "$CurMount/etc/initramfs-tools/scripts/local-bottom/unlock_drive2"
#!/bin/sh
PREREQ=""

prereqs()
{
   echo "\$PREREQ"
}

case "\$1" in
   prereqs)
       prereqs
       exit 0
       ;;
esac

. /scripts/functions
cryptroot-unlock
vgchange -ay "$CurCryptVol"
mkdir -p /mnt/keyfile
mount "/dev/$CurCryptVol/$CurCryptExtra" /mnt/keyfile
cryptsetup luksOpen /dev/disk/by-uuid/$Drive2_UUID "$NewCryptName" "--key-file=/mnt/keyfile/$DriveKeyName"
umount /mnt/keyfile
rmdir /mnt/keyfile

mount -t btrfs -o "subvol=$CurCryptRootSubVol" "/dev/$CurCryptVol/$CurCryptRoot" /root

#If you are weird like me and /usr is stored elsewhere, here is where you would need to mount it.
#It cannot be done through your fstab in this setup.
#mount --bind /root/sub/sys/usr /root/usr

mount --bind /dev /root/dev
mount --bind /proc /root/proc
mount --bind /sys /root/sys
EOF

chmod 755 "$CurMount/etc/initramfs-tools/scripts/local-bottom/unlock_drive2"

echo "--------------------Setup booting from the root file system-------------------"
echo "Prepare a chroot environment"
for i in dev dev/pts proc sys run tmp; do
 mount -o bind /$i "$CurMount/$i"
done

echo "Run commands in the chroot environment to update initramfs and grub"
chroot "$CurMount" <<EOF
echo "Mount the other partitions (specifically for “boot” and “boot/efi”)"
mount -a

echo "Update initramfs and grub"
update-initramfs -u -k all
update-grub
EOF

echo "-----------------------------------Finish up----------------------------------"
echo "Reboot and pray"
reboot


 9 
 on: November 20, 2024, 09:58:02 pm 
Started by Dakusan - Last post by Dakusan

I added a live “total progress” progress bar to the MD5Sum List Script. It utilizes a temp fifo file and a backgrounded “pv”.


 10 
 on: October 19, 2024, 05:22:25 am 
Started by Dakusan - Last post by Dakusan

I wanted to make a script to check if updated flatpak app permissions have changed before updating them. I decided to use ChatGPTs newest model, 01-preview for this task and holy crap am I impressed. I had to give it 14 commands to get to the final product and it seems to work great. That number could have been reduced quite a bit had I done things a bit differently.

I still had to find the problems, look up reference docs to correct it, and even debug in an IDE a little. But just telling it where the problems were, it got there in the end, and its user interaction output is way better than what I was planning on doing.

Sections:

My final updated copy of the script

<?php
// -- This script checks for Flatpak updates and reports permission changes. --

// Function to parse permissions from Flatpak metadata
function parsePermissions($content) {
   $permissions = [];
   $lines = is_array($content) ? $content : explode("\n", $content);
   $currentSection = '';
   $skipSections = ['Application']; // Sections to skip

   foreach ($lines as $line) {
       $line = trim($line);

       if (empty($line)) {
           continue;
       }

       // Check for section headers
       if (preg_match('/^\[(.*)\]$/', $line, $matches)) {
           $currentSection = $matches[1];

           // Skip the [Application] section
           if (in_array($currentSection, $skipSections)) {
               $currentSection = '';
               continue;
           }

           $permissions[$currentSection] = [];
       } elseif ($currentSection !== '') {
           // Only process lines within non-skipped sections
           $parts = explode('=', $line, 2);
           if (count($parts) == 2) {
               $key = $parts[0];
               $values = explode(';', trim($parts[1], ';'));
               $permissions[$currentSection][$key] = $values;
           } else {
               // Handle keys without '=' (e.g., single permissions)
               $permissions[$currentSection][$line] = [];
           }
       }
   }

   return $permissions;
}

// Function to compare permissions
function comparePermissions($current, $new) {
   $differences = [];

   // Get all sections
   $sections = array_unique(array_merge(array_keys($current), array_keys($new)));

   foreach ($sections as $section) {
       $currentSection = isset($current[$section]) ? $current[$section] : [];
       $newSection = isset($new[$section]) ? $new[$section] : [];

       // Get all keys in this section
       $keys = array_unique(array_merge(array_keys($currentSection), array_keys($newSection)));

       foreach ($keys as $key) {
           $currentValues = isset($currentSection[$key]) ? $currentSection[$key] : [];
           $newValues = isset($newSection[$key]) ? $newSection[$key] : [];

           // Compare values
           $added = array_diff($newValues, $currentValues);
           $removed = array_diff($currentValues, $newValues);

           if (!empty($added) || !empty($removed)) {
               $differences[$section][$key] = [
                   'added' => $added,
                   'removed' => $removed,
               ];
           }
       }
   }

   return $differences;
}

// Function to output a line with appId (if given), colored red (IsError=true) or green (IsError=false)
function outputColor($appId, $str, $isError=true, $NoNewline=false)
{
   // Determine if coloring should be used
   static $hasColors=null;
   if(!isset($hasColors)) {
       $hasColors=stream_isatty(STDOUT) && (!trim(`command -v tput`) || intval(`tput colors`)>=16);
   }

   echo
       (!$hasColors ? '' : ($isError ? "\e[31m" : "\e[32m")).
       ($appId ? "$appId: " : '').
       $str.
       (!$hasColors ? '' : "\e[0m").
       ($NoNewline ? '' : "\n");
}

// Function to display permission differences
function displayDifferences($appId, $differences) {
   outputColor($appId, 'Permission changes:');
   foreach ($differences as $section => $keys) {
       outputColor(null, "  [$section]");
       foreach ($keys as $key => $changes) {
           if (!empty($changes['added'])) {
               outputColor(null, "    + $key = " . implode(';', $changes['added']));
           }
           if (!empty($changes['removed'])) {
               outputColor(null, "    - $key = " . implode(';', $changes['removed']));
           }
       }
   }
   echo "\n";
}

// Function to ask the user a question
function askQuestion($Question) {
   outputColor('', "$Question (y/N): ", true, true);
   $handle = fopen('php://stdin', 'r');
   $line = fgets($handle);
   $answer = trim(strtolower($line));
   fclose($handle);
   return ($answer == 'y' || $answer == 'yes');
}

// Get architecture (moved above the loop)
$archCommand = 'flatpak --default-arch';
exec($archCommand, $archOutput);
$arch = trim(implode('', $archOutput));

// Step 1: Get the list of installed Flatpaks
$installedCommand = 'flatpak list -a --columns=application,branch,origin,options';
exec($installedCommand, $installedOutput);

// Remove header line if present
if (!empty($installedOutput) && strpos($installedOutput[0], 'Application ID') !== false) {
   array_shift($installedOutput);
}

// Build an associative array of installed applications
$installedApps = [];

foreach ($installedOutput as $line) {
   // The output is tab-delimited
   $columns = explode("\t", trim($line));
   if (count($columns) >= 4) {
       $appId = $columns[0];
       $branch = $columns[1];
       $origin = $columns[2];
       $options = $columns[3];

       $installedApps[$appId] = [
           'appId' => $appId,
           'branch' => $branch,
           'origin' => $origin,
           'options' => $options,
       ];
   }
}

// Get the list of updates
exec('echo n | flatpak update', $updatesOutput);
foreach ($updatesOutput as $Line) {
   if (preg_match('/^ *\d+\.\s+([\w\.-]+)/', $Line, $matches)) {
       $updatesAvailable[]=$matches[1];
   }
}

// Let the user confirm the updates
echo str_repeat('-', 80)."\n";
echo implode("\n", $updatesOutput)."\n";
echo str_repeat('-', 80)."\n";
if (empty($updatesAvailable)) {
   outputColor('', 'No updates available for installed Flatpaks.');
   exit(0);
}
if (!askQuestion('Found '.count($updatesAvailable).' updates. Continue?')) {
   echo "Updates canceled.\n";
   exit(0);
}

$permissionChanges = [];
foreach ($updatesAvailable as $appId) {
   if (!isset($installedApps[$appId])) {
       outputColor($appId, 'Installed app not found. Skipping.');
       continue;
   }

   $app = $installedApps[$appId];
   $branch = $app['branch'];
   $origin = $app['origin'];
   $options = $app['options'];

   // Determine if it's an app or runtime
   $isRuntime = strpos($options, 'runtime') !== false;

   // Paths to the metadata files
   if ($isRuntime) {
       $metadataPath = "/var/lib/flatpak/runtime/$appId/$arch/$branch/active/metadata";
   } else {
       $metadataPath = "/var/lib/flatpak/app/$appId/$arch/$branch/active/metadata";
   }

   // Check if the metadata file exists
   if (!file_exists($metadataPath)) {
       outputColor($appId, 'Metadata file not found. Skipping.');
       continue;
   }

   // Read current permissions from the metadata file
   $metadataContent = file_get_contents($metadataPath);
   $currentPermissions = parsePermissions($metadataContent);

   // Get new metadata from remote
   $ref = $appId . '/' . $arch . '/' . $branch;
   $remoteInfoCommand = 'flatpak remote-info --show-metadata ' . escapeshellarg($origin) . ' ' . escapeshellarg($ref);

   // Clear $remoteOutput before exec()
   $remoteOutput = [];
   exec($remoteInfoCommand, $remoteOutput);

   if (empty($remoteOutput)) {
       outputColor($appId, 'Failed to retrieve remote metadata. Skipping.');
       continue;
   }

   // Parse new permissions from the remote metadata
   $newPermissions = parsePermissions($remoteOutput);

   // Compare permissions
   $differences = comparePermissions($currentPermissions, $newPermissions);

   if (!empty($differences)) {
       $permissionChanges[$appId] = $differences;
       displayDifferences($appId, $differences);
   } else {
       outputColor($appId, 'No permission changes found.', false);
   }
}

// If there are no permission changes, inform the user
if (empty($permissionChanges)) {
   echo "No permission changes detected in the available updates.\n";
}

// Ask user if they want to proceed
if (askQuestion('Do you want to proceed with the updates?')) {
   // Proceed with updates
   echo "Updating Flatpaks...\n";
   passthru('flatpak update -y');
} else {
   echo "Updates canceled.\n";
}
?>
List of the changes I made from the AI produced script
  • Added “outputColor” function and wrapped appropriate echo statements into it
  • Added “-a” to “flatpak list”
  • Added output line “Installed app not found. Skipping”
  • Added output line “No permission changes found.”
  • Added “askQuestion” function since there are now multiple places in the script that ask questions
  • Changed get-updates command from “flatpak remote-ls --updates” to “echo n | flatpak update” (with a regex extraction). Also now confirms the update list with the user.
The commands I gave the AI

For demonstration purposes, I have left all of my original typos uncorrected below.


Write me a program in php that checks if there are any updates for my installed flatpaks. and then for each flatpak check and see if the permissions have changed between my current version and the new version. report to me the changes. then ask me if i want to continue with the updates.
your code doesnt work.
first $command = 'flatpak list --app --updates --columns=application,app,branch,origin,installation'; should be flatpak remote-ls --updates --columns=application,app,branch,origin (installation is an invalid column)
and then flatpak info --show-permissions doesnt give the original permissions. you have to get the original non-user-modified permissions from /var/lib/flatpak
the line if (empty($output)) { is incorrect, it should be if (count($output)) { as $output is an array, not a string
$columns = preg_split('/\s{2,}/', trim($line)); is incorrect. its tab delimited, so it just needs to be explode("\t", trim($line));
you also have a problem with the $metadataPath. its not always in /var/lib/flatpak/app/ . It may be in /var/lib/flatpak/runtime/. You can confirm which directory it should be in with the options column having "runtime" as an option.
in the --columns for your first command, "application" should be "name"
actually, you cant get the options from flatpak remote-ls --updates . you have to get the options from flatpak list
flatpak remote-info does not have an option --show-permissions
in your flatpak remote-info command, there is no --branch option.
there is no --assumeno option
there is no --dry-run permission. perhaps use --no-deploy?
you are incorrect. flatpak remote-info does not have an option --show-permissions . we already went over that. perhaps flatpak remote-info --show-metadata to get the permissions
flatpak remote-info does not have --branch
when checking the metadata, do not compare the info under [Application]
also, move the "Get architecture" line above its for loop
you have a problem with exec($remoteInfoCommand, $remoteOutput); you need to always empty $remoteOutput before running the exec. Otherwise, it appends the data to what is already there.


The final script the AI produced

<?php
/**
* This script checks for Flatpak updates and reports permission changes.
*/

// Function to parse permissions from Flatpak metadata
function parsePermissions($content) {
   $permissions = [];
   $lines = is_array($content) ? $content : explode("\n", $content);
   $currentSection = '';
   $skipSections = ['Application']; // Sections to skip

   foreach ($lines as $line) {
       $line = trim($line);

       if (empty($line)) {
           continue;
       }

       // Check for section headers
       if (preg_match('/^\[(.*)\]$/', $line, $matches)) {
           $currentSection = $matches[1];

           // Skip the [Application] section
           if (in_array($currentSection, $skipSections)) {
               $currentSection = '';
               continue;
           }

           $permissions[$currentSection] = [];
       } elseif ($currentSection !== '') {
           // Only process lines within non-skipped sections
           $parts = explode('=', $line, 2);
           if (count($parts) == 2) {
               $key = $parts[0];
               $values = explode(';', trim($parts[1], ';'));
               $permissions[$currentSection][$key] = $values;
           } else {
               // Handle keys without '=' (e.g., single permissions)
               $permissions[$currentSection][$line] = [];
           }
       }
   }

   return $permissions;
}

// Function to compare permissions
function comparePermissions($current, $new) {
   $differences = [];

   // Get all sections
   $sections = array_unique(array_merge(array_keys($current), array_keys($new)));

   foreach ($sections as $section) {
       $currentSection = isset($current[$section]) ? $current[$section] : [];
       $newSection = isset($new[$section]) ? $new[$section] : [];

       // Get all keys in this section
       $keys = array_unique(array_merge(array_keys($currentSection), array_keys($newSection)));

       foreach ($keys as $key) {
           $currentValues = isset($currentSection[$key]) ? $currentSection[$key] : [];
           $newValues = isset($newSection[$key]) ? $newSection[$key] : [];

           // Compare values
           $added = array_diff($newValues, $currentValues);
           $removed = array_diff($currentValues, $newValues);

           if (!empty($added) || !empty($removed)) {
               $differences[$section][$key] = [
                   'added' => $added,
                   'removed' => $removed,
               ];
           }
       }
   }

   return $differences;
}

// Function to display permission differences
function displayDifferences($appId, $differences) {
   echo "Permission changes for $appId:\n";
   foreach ($differences as $section => $keys) {
       echo "  [$section]\n";
       foreach ($keys as $key => $changes) {
           if (!empty($changes['added'])) {
               echo "    + $key = " . implode(';', $changes['added']) . "\n";
           }
           if (!empty($changes['removed'])) {
               echo "    - $key = " . implode(';', $changes['removed']) . "\n";
           }
       }
   }
   echo "\n";
}

// Get architecture (moved above the loop)
$archCommand = 'flatpak --default-arch';
exec($archCommand, $archOutput);
$arch = trim(implode('', $archOutput));

// Step 1: Get the list of installed Flatpaks
$installedCommand = 'flatpak list --columns=application,branch,origin,options';
exec($installedCommand, $installedOutput);

// Remove header line if present
if (!empty($installedOutput) && strpos($installedOutput[0], 'Application ID') !== false) {
   array_shift($installedOutput);
}

// Build an associative array of installed applications
$installedApps = [];

foreach ($installedOutput as $line) {
   // The output is tab-delimited
   $columns = explode("\t", trim($line));
   if (count($columns) >= 4) {
       $appId = $columns[0];
       $branch = $columns[1];
       $origin = $columns[2];
       $options = $columns[3];

       $installedApps[$appId] = [
           'appId' => $appId,
           'branch' => $branch,
           'origin' => $origin,
           'options' => $options,
       ];
   }
}

// Get the list of available updates
$updatesCommand = 'flatpak remote-ls --updates --columns=application';
exec($updatesCommand, $updatesOutput);

// Remove header line if present
if (!empty($updatesOutput) && strpos($updatesOutput[0], 'Application ID') !== false) {
   array_shift($updatesOutput);
}

// Build a list of applications that have updates
$updatesAvailable = array_map('trim', $updatesOutput);

if (empty($updatesAvailable)) {
   echo "No updates available for installed Flatpaks.\n";
   exit(0);
}

$permissionChanges = [];

foreach ($updatesAvailable as $appId) {
   if (!isset($installedApps[$appId])) {
       // The updated app is not in the installed apps list, skip it
       continue;
   }

   $app = $installedApps[$appId];
   $branch = $app['branch'];
   $origin = $app['origin'];
   $options = $app['options'];

   // Determine if it's an app or runtime
   $isRuntime = strpos($options, 'runtime') !== false;

   // Paths to the metadata files
   if ($isRuntime) {
       $metadataPath = "/var/lib/flatpak/runtime/$appId/$arch/$branch/active/metadata";
   } else {
       $metadataPath = "/var/lib/flatpak/app/$appId/$arch/$branch/active/metadata";
   }

   // Check if the metadata file exists
   if (!file_exists($metadataPath)) {
       echo "Metadata file not found for $appId. Skipping.\n";
       continue;
   }

   // Read current permissions from the metadata file
   $metadataContent = file_get_contents($metadataPath);
   $currentPermissions = parsePermissions($metadataContent);

   // Get new metadata from remote
   $ref = $appId . '/' . $arch . '/' . $branch;
   $remoteInfoCommand = 'flatpak remote-info --show-metadata ' . escapeshellarg($origin) . ' ' . escapeshellarg($ref);

   // Clear $remoteOutput before exec()
   $remoteOutput = [];
   exec($remoteInfoCommand, $remoteOutput);

   if (empty($remoteOutput)) {
       echo "Failed to retrieve remote metadata for $appId. Skipping.\n";
       continue;
   }

   // Parse new permissions from the remote metadata
   $newPermissions = parsePermissions($remoteOutput);

   // Compare permissions
   $differences = comparePermissions($currentPermissions, $newPermissions);

   if (!empty($differences)) {
       $permissionChanges[$appId] = $differences;
       displayDifferences($appId, $differences);
   }
}

// If there are no permission changes, inform the user
if (empty($permissionChanges)) {
   echo "No permission changes detected in the available updates.\n";
}

// Ask user if they want to proceed
echo "Do you want to proceed with the updates? (y/N): ";
$handle = fopen("php://stdin", "r");
$line = fgets($handle);
$answer = trim(strtolower($line));

if ($answer == 'y' || $answer == 'yes') {
   // Proceed with updates
   echo "Updating Flatpaks...\n";
   passthru('flatpak update -y');
} else {
   echo "Updates canceled.\n";
}

fclose($handle);
?>

Pages: [1] 2 3 ... 10