memory-leaks

Still reachable: lots of words in many pages.
git clone https://git.kevinlegouguec.net/memory-leaks
Log | Files | Refs | README | LICENSE

apps.org (8381B)


      1 #+TITLE: Installing applications under =$HOME=
      2 
      3 One day I woke up and decided to stop letting ~make install~ put
      4 programs under =/usr/local/=.  Not every project provides an
      5 =uninstall= target, so over time, as more and more applications get
      6 lumped under that hierarchy, it becomes a clutter of stuff that I
      7 cannot remove without…
      8 
      9 1. guesstimating what files belong to any given program using
     10    timestamps,
     11 2. weighing my odds of passing correctly crafted globs to the likes of
     12    =sudo find -delete= and =sudo rm -r= without destroying my system.
     13 
     14 Instead I figured I would start ~./configure~'ing programs with
     15 =--prefix=${HOME}/apps/${program}=, and teach whichever dotfiles
     16 manage session environment variables to arrange for installed
     17 resources to be found.
     18 
     19 How hard could it be?
     20 
     21 * =PATH=
     22 The Correct Way™ to set environment variables seems to depend on
     23 
     24 1. the kind of session (TTY vs GUI),
     25 2. the init system (e.g. [[https://www.freedesktop.org/software/systemd/man/environment.d.html][systemd user environment variables]])
     26 3. the desktop environment (e.g. [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]),
     27 4. the display server,
     28 5. the distro?
     29    - e.g. Debian's SSDM [[https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=794419][used not to]] source =~/.profile=,
     30    - and [[https://github.com/sddm/sddm/issues/448][developers said]] it should not anyway,
     31    - except [[https://github.com/sddm/sddm/issues/1551][it has provisions to do so]]?
     32 6. [[https://man7.org/linux/man-pages/man8/pam_env.8.html][PAM]]?
     33 
     34 Empirically with Plasma on both Tumbleweed (20230929) and Debian 12,
     35 =~/.profile= seems to be run for both TTY and GUI sessions.  Unclear
     36 what arcane magic is causing that: probably the login manager - on
     37 Tumbleweed =/usr/share/sddm/scripts/Xsession= seems to start a ~bash
     38 --login~, but getting lost in the =/usr/etc/X11/xdm/= weeds trying to
     39 track down who might source that =Xesssion= script.
     40 
     41 =~/.profile= it is, then 🤞
     42 
     43 (For other DEs on X, my recollection was that =~/.xsessionrc= is the
     44 way to go for GUI sessions?  No clue about Wayland)
     45 
     46 * ~man~ pages
     47 =manpath(5)= says:
     48 
     49 #+begin_quote
     50 By default, man-db examines the user's =$PATH=.  For each
     51 =path_element= found there, it adds =manpath_element= to the search
     52 path.
     53 
     54 If there is no =MANPATH_MAP= line in the configuration file for a
     55 given =path_element=, then it adds all of =path_element/../man=,
     56 =path_element/man=, =path_element/../share=man=, and
     57 =path_element/shared/=man= that exist as directories to the search
     58 path.
     59 #+end_quote
     60 
     61 How convenient!  Setting =PATH= automatically takes care of ~man~
     62 then… unless =MANPATH= is set, in which case that logic is bypassed.
     63 
     64 ** openSUSE
     65 =/usr/etc/profile.d/manpath.sh= [fn:: sourced by =/etc/profile=,
     66 sourced by =/usr/etc/profile= and =~/.profile=.] plays a little danse
     67 to (a) query ~manpath~, which runs the heuristic described above (b)
     68 set the resulting value "in stone" by exporting =MANPATH=.
     69 
     70 This means that ~man~ will not consider any =PATH= element added by
     71 the user after that profile fragment was sourced.
     72 
     73 Solutions:
     74 
     75 1. Unset =MANPATH= in =~/.profile=, after =/etc/profile= is read.
     76    =~/.profile= says it is "read each time a login shell is started.
     77    All other interactive shells will only read .bashrc", so unclear
     78    whether that will affect graphical sessions.
     79 2. Extend =PATH= with apps /before/ those profile fragments are
     80    loaded.  Unclear how to accomplish that:
     81    - [[https://wiki.archlinux.org/title/environment_variables#Per_user][systemd user environment variables]]?  Seems to be limited to
     82      =KEY=VALUE= directives; I would like to run a bit of logic that
     83      will automatically add every =~/apps/*/bin= directory.
     84    - [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]?  Presumably will not affect
     85      TTYs?
     86 3. Embrace the distro's method: if =MANPATH= is set, extend it.
     87 
     88 Went with solution 1 because empirically =~/.profile= seems to be
     89 sourced for GUI sessions (as noted in § =PATH=).
     90 
     91 * ~info~ pages
     92 Similar to ~man~, ~info~ knows to work with =PATH=.  The priority
     93 [[https://git.savannah.gnu.org/cgit/texinfo.git/tree/info/infopath.c?h=texinfo-7.0.3#n41][seems to be]]:
     94 
     95 1. the =INFOPATH= environment variable,
     96 2. the =INFODIR= build macro, /if =infopath-no-defaults= is not set/,
     97 3. the =DEFAULT_INFOPATH= build macro, /if =INFOPATH= is unset or ends
     98    with a trailing colon/.
     99 
    100 Any of these can contain the =PATH= literal, which means "iterate over
    101 =${PATH}= and add nearby =share/info= & =info= directories".  By
    102 default,
    103 
    104 - =DEFAULT_INFOPATH= contains =PATH= 🥳
    105 - *but* =INFODIR= has priority and is set to something boring like
    106   =/usr/share/info= 😒
    107 
    108 Solution:
    109 
    110 #+begin_src bash
    111 cat <<EOF > ~/.infokey
    112 #var
    113 # Disable INFODIR: fall back to DEFAULT_INFOPATH, which contains
    114 # 'PATH', prioritizing program PATH over /usr/share/info.
    115 infopath-no-defaults=On
    116 EOF
    117 #+end_src
    118 
    119 * DONE systemd services
    120 =systemd.unit(5)= § "User Unit Search Path" mentions a couple of
    121 variables that we could append:
    122 
    123 - =$XDG_CONFIG_DIRS/systemd/user/*=
    124 - =$XDG_DATA_DIRS/systemd/user/*=
    125 
    126 And:
    127 
    128 #+begin_quote
    129 When the variable =$SYSTEMD_UNIT_PATH= is set, the contents of this
    130 variable overrides the unit load path.  If =$SYSTEMD_UNIT_PATH= ends
    131 with an empty component (=:=), the usual unit load path will be
    132 appended to the contents of the variable.
    133 #+end_quote
    134 
    135 So we could either:
    136 
    137 1. find the directory 𝒟 where an app tucks =systemd/user= in its
    138    installation tree, and append 𝒟 to one of the XDG variables,
    139 2. find the directory where an app tucks its =.service= files, and
    140    append that directory to =SYSTEMD_UNIT_PATH=, keeping a final
    141    colon.
    142 
    143 (1) could feed multiple birds with one scone: in particular,
    144 =XDG_DATA_DIRS= is used for other purposes (see § Desktop entry
    145 files).  Serendipitously, [[https://github.com/systemd/systemd/blob/1d87a00a951dca801c8ccd79c1460fa91efa7dce/src/basic/path-lookup.c#L147][the systemd sources]] suggest =DATA= over
    146 =CONFIG= for our purposes:
    147 
    148 #+begin_src c
    149         /* Implement the mechanisms defined in
    150          *
    151          * https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
    152          *
    153          * We look in both the config and the data dirs because we
    154          * want to encourage that distributors ship their unit files
    155          * as data, and allow overriding as configuration.
    156          */
    157 #+end_src
    158 
    159 * DONE Desktop entry files
    160 The Freedesktop [[https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html][Desktop Menu]] and [[https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html][Desktop Entry]] specs spell out how
    161 =XDG_DATA_DIRS= will be searched for =applications/*.desktop= files.
    162 
    163 * TODO ~bash~ completions
    164 
    165 * Putting it all together
    166 Presenting [[./apps-demo/activate][=~/apps/activate=]], to be sourced from =~/.profile=:
    167 
    168 #+INCLUDE: "apps-demo/activate" src sh
    169 
    170 * Resources
    171 ** [[https://nullprogram.com/blog/2017/06/19/][nullprogram.com]] — Building and Installing Software in $HOME
    172 Explains how to set things up so that one can use =~/.local= as a
    173 first-class citizen for applications, libraries and development
    174 resources (headers, pkg-config, manpages).
    175 
    176 ** [[https://specifications.freedesktop.org][specifications.freedesktop.org]] — Freedesktop.org Specifications
    177 Hosts the specifications for the behavior and environment variables
    178 most GNU/Linux distros (that I have experience with) adhere to, e.g.
    179 
    180 - the [[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html][Base Directory spec]] :: defines the various
    181   =XDG_*_@(HOME|DIR|DIRS)= variables referenced here;
    182 - the [[https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html][Desktop Entry spec]] :: explains the significance of a desktop
    183   file's filename with respect to its position within =XDG_DATA_DIRS=;
    184 - the [[https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html][Desktop Menu spec]] :: defines the expected file locations for
    185   desktop files (and more) with respect to XDG base directories.
    186 
    187 ** [[https://wiki.archlinux.org/title/Environment_variables#Per_user][wiki.archlinux.org]] — Environment variables § Per user - ArchWiki
    188 A thorough overview of the myriad of ways users can amend environment
    189 variables, as examined in § PATH.