···4949 imagemagick
5050 zip
5151 ];
5252+ execer = [
5353+ # zip can exec; confirmed 2 invocations in pdf2odt don't
5454+ "cannot:${zip}/bin/zip"
5555+ ];
5256 };
53575458 meta = with lib; {
+10
pkgs/development/libraries/libarchive/default.nix
···2323, cmake
2424, nix
2525, samba
2626+2727+# for passthru.lore
2828+, binlore
2629}:
27302831assert xarSupport -> libxml2 != null;
···125128 passthru.tests = {
126129 inherit cmake nix samba;
127130 };
131131+132132+ # bsdtar is detected as "cannot" because its exec is internal to
133133+ # calls it makes into libarchive itself. If binlore gains support
134134+ # for detecting another layer down into libraries, this can be cut.
135135+ passthru.binlore.out = binlore.synthesize finalAttrs.finalPackage ''
136136+ execer can bin/bsdtar
137137+ '';
128138})
+12
pkgs/development/libraries/ncurses/default.nix
···1111, mouseSupport ? false, gpm
1212, unicodeSupport ? true
1313, testers
1414+, binlore
1415}:
15161617stdenv.mkDerivation (finalAttrs: {
···178179179180 preFixup = lib.optionalString (!stdenv.hostPlatform.isCygwin && !enableStatic) ''
180181 rm "$out"/lib/*.a
182182+ '';
183183+184184+ # I'm not very familiar with ncurses, but it looks like most of the
185185+ # exec here will run hard-coded executables. There's one that is
186186+ # dynamic, but it looks like it only comes from executing a terminfo
187187+ # file, so I think it isn't going to be under user control via CLI?
188188+ # Happy to have someone help nail this down in either direction!
189189+ # The "capability" is 'iprog', and I could only find 1 real example:
190190+ # https://invisible-island.net/ncurses/terminfo.ti.html#tic-linux-s
191191+ passthru.binlore.out = binlore.synthesize ncurses ''
192192+ execer cannot bin/{reset,tput,tset}
181193 '';
182194183195 meta = with lib; {
···5656 # in here, but I'm erring on the side of flexibility
5757 # since this form will make it easier to pilot other
5858 # uses of binlore.
5959- callback = lore: drv: overrides: ''
5959+ callback = lore: drv: ''
6060 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
6161 echo generating binlore for $drv by running:
6262 echo "${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}"
6363 else
6464 echo "failed to generate binlore for $drv (none of ${drv}/{bin,lib,libexec} exist)"
6565 fi
6666- '' +
6767- /*
6868- Override lore for some packages. Unsure, but for now:
6969- 1. start with the ~name (pname-version)
7070- 2. remove characters from the end until we find a match
7171- in overrides/
7272- 3. execute the override script with the list of expected
7373- lore types
7474- */
7575- ''
7676- i=''${#identifier}
7777- filter=
7878- while [[ $i > 0 ]] && [[ -z "$filter" ]]; do
7979- if [[ -f "${overrides}/''${identifier:0:$i}" ]]; then
8080- filter="${overrides}/''${identifier:0:$i}"
8181- echo using "${overrides}/''${identifier:0:$i}" to generate overriden binlore for $drv
8282- break
8383- fi
8484- ((i--)) || true # don't break build
8585- done # || true # don't break build
6666+8667 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
8787- ${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback} "$filter"
6868+ ${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}
8869 fi
8970 '';
9071 };
9191- overrides = (src + "/overrides");
92729373in rec {
7474+ /*
7575+ Output a directory containing lore for multiple drvs.
7676+7777+ This will `make` lore for drv in drvs and then combine lore
7878+ of the same type across all packages into a single file.
7979+8080+ When drvs are also specified in the strip argument, corresponding
8181+ lore is made relative by stripping the path of each drv from
8282+ matching entries. (This is mainly useful in a build process that
8383+ uses a chain of two or more derivations where the output of one
8484+ is the source for the next. See resholve for an example.)
8585+ */
9486 collect = { lore ? loreDef, drvs, strip ? [ ] }: (runCommand "more-binlore" { } ''
9587 mkdir $out
9688 for lorefile in ${toString lore.types}; do
9789 cat ${lib.concatMapStrings (x: x + "/$lorefile ") (map (make lore) (map lib.getBin (builtins.filter lib.isDerivation drvs)))} > $out/$lorefile
9898- substituteInPlace $out/$lorefile ${lib.concatMapStrings (x: "--replace '${x}/' '' ") strip}
9090+ substituteInPlace $out/$lorefile ${lib.concatMapStrings (x: "--replace-quiet '${x}/' '' ") strip}
9991 done
10092 '');
101101- # TODO: echo for debug, can be removed at some point
9393+9494+ /*
9595+ Output a directory containing lore for a single drv.
9696+9797+ This produces lore for the derivation (via lore.callback) and
9898+ appends any lore that the derivation itself wrote to nix-support
9999+ or which was overridden in drv.binlore.<outputName> (passthru).
100100+101101+ > *Note*: Since the passthru is attached to all outputs, binlore
102102+ > is an attrset namespaced by outputName to support packages with
103103+ > executables in more than one output.
104104+105105+ Since the last entry wins, the effective priority is:
106106+ drv.binlore.<outputName> > $drv/nix-support > lore generated here by callback
107107+ */
102108 make = lore: drv: runCommand "${drv.name}-binlore" {
103103- identifier = drv.name;
104109 drv = drv;
105110 } (''
106111 mkdir $out
107112 touch $out/{${builtins.concatStringsSep "," lore.types}}
108113109109- ${lore.callback lore drv overrides}
114114+ ${lore.callback lore drv}
115115+ '' +
116116+ # append lore from package's $out and drv.binlore.${drv.outputName} (last entry wins)
117117+ ''
118118+ for lore_type in ${builtins.toString lore.types}; do
119119+ if [[ -f "${drv}/nix-support/$lore_type" ]]; then
120120+ cat "${drv}/nix-support/$lore_type" >> "$out/$lore_type"
121121+ fi
122122+ '' + lib.optionalString (builtins.hasAttr "binlore" drv && builtins.hasAttr drv.outputName drv.binlore) ''
123123+ if [[ -f "${drv.binlore."${drv.outputName}"}/$lore_type" ]]; then
124124+ cat "${drv.binlore."${drv.outputName}"}/$lore_type" >> "$out/$lore_type"
125125+ fi
126126+ '' + ''
127127+ done
110128111129 echo binlore for $drv written to $out
112130 '');
131131+132132+ /*
133133+ Utility function for creating override lore for drv.
134134+135135+ We normally attach this lore to `drv.passthru.binlore.<outputName>`.
136136+137137+ > *Notes*:
138138+ > - Since the passthru is attached to all outputs, binlore is an
139139+ > attrset namespaced by outputName to support packages with
140140+ > executables in more than one output. You'll generally just use
141141+ > `out` or `bin`.
142142+ > - We can reconsider the passthru attr name if someone adds
143143+ > a new lore provider. We settled on `.binlore` for now to make it
144144+ > easier for people to figure out what this is for.
145145+146146+ The lore argument should be a Shell script (string) that generates
147147+ the necessary lore. You can use arbitrary Shell, but this function
148148+ includes a shell DSL you can use to declare/generate lore in most
149149+ cases. It has the following functions:
150150+151151+ - `execer <verdict> [<path>...]`
152152+ - `wrapper <wrapper_path> <original_path>`
153153+154154+ Writing every override explicitly in a Nix list would be tedious
155155+ for large packages, but this small shell DSL enables us to express
156156+ many overrides efficiently via pathname expansion/globbing.
157157+158158+ Here's a very general example of both functions:
159159+160160+ passthru.binlore.out = binlore.synthesize finalAttrs.finalPackage ''
161161+ execer can bin/hello bin/{a,b,c}
162162+ wrapper bin/hello bin/.hello-wrapped
163163+ '';
164164+165165+ And here's a specific example of how pathname expansion enables us
166166+ to express lore for the single-binary variant of coreutils while
167167+ being both explicit and (somewhat) efficient:
168168+169169+ passthru = {} // optionalAttrs (singleBinary != false) {
170170+ binlore.out = binlore.synthesize coreutils ''
171171+ execer can bin/{chroot,env,install,nice,nohup,runcon,sort,split,stdbuf,timeout}
172172+ execer cannot bin/{[,b2sum,base32,base64,basename,basenc,cat,chcon,chgrp,chmod,chown,cksum,comm,cp,csplit,cut,date,dd,df,dir,dircolors,dirname,du,echo,expand,expr,factor,false,fmt,fold,groups,head,hostid,id,join,kill,link,ln,logname,ls,md5sum,mkdir,mkfifo,mknod,mktemp,mv,nl,nproc,numfmt,od,paste,pathchk,pinky,pr,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,seq,sha1sum,sha224sum,sha256sum,sha384sum,sha512sum,shred,shuf,sleep,stat,stty,sum,sync,tac,tail,tee,test,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,vdir,wc,who,whoami,yes}
173173+ '';
174174+ };
175175+176176+ Caution: Be thoughtful about using a bare wildcard (*) glob here.
177177+ We should generally override lore only when a human understands if
178178+ the executable will exec arbitrary user-passed executables. A bare
179179+ glob can match new executables added in future package versions
180180+ before anyone can audit them.
181181+ */
182182+ synthesize = drv: loreSynthesizingScript: runCommand "${drv.name}-lore-override" {
183183+ drv = drv;
184184+ } (''
185185+ execer(){
186186+ local verdict="$1"
187187+188188+ shift
189189+190190+ for path in "$@"; do
191191+ if [[ -f "$PWD/$path" ]]; then
192192+ echo "$verdict:$PWD/$path"
193193+ else
194194+ echo "error: Tried to synthesize execer lore for missing file: $PWD/$path" >&2
195195+ exit 2
196196+ fi
197197+ done
198198+ } >> $out/execers
199199+200200+ wrapper(){
201201+ local wrapper="$1"
202202+ local original="$2"
203203+204204+ if [[ ! -f "$wrapper" ]]; then
205205+ echo "error: Tried to synthesize wrapper lore for missing wrapper: $PWD/$wrapper" >&2
206206+ exit 2
207207+ fi
208208+209209+ if [[ ! -f "$original" ]]; then
210210+ echo "error: Tried to synthesize wrapper lore for missing original: $PWD/$original" >&2
211211+ exit 2
212212+ fi
213213+214214+ echo "$PWD/$wrapper:$PWD/$original"
215215+216216+ } >> $out/wrappers
217217+218218+ mkdir $out
219219+220220+ # lore override commands are relative to the drv root
221221+ cd $drv
222222+223223+ '' + loreSynthesizingScript);
113224}
+9
pkgs/os-specific/linux/nixos-rebuild/default.nix
···1010, lib
1111, nixosTests
1212, installShellFiles
1313+, binlore
1414+, nixos-rebuild
1315}:
1416let
1517 fallback = import ./../../../../nixos/modules/installer/tools/nix-fallback-paths.nix;
···4850 specialisations = nixosTests.nixos-rebuild-specialisations;
4951 target-host = nixosTests.nixos-rebuild-target-host;
5052 };
5353+5454+ # nixos-rebuild can’t execute its arguments
5555+ # (but it can run ssh with the with the options stored in $NIX_SSHOPTS,
5656+ # and ssh can execute its arguments...)
5757+ passthru.binlore.out = binlore.synthesize nixos-rebuild ''
5858+ execer cannot bin/nixos-rebuild
5959+ '';
51605261 meta = {
5362 description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote";
+9
pkgs/os-specific/linux/procps-ng/default.nix
···1515 # exception is ‘watch’ which is portable enough to run on pretty much
1616 # any UNIX-compatible system.
1717, watchOnly ? !(stdenv.isLinux || stdenv.isCygwin)
1818+1919+, binlore
2020+, procps
1821}:
19222023stdenv.mkDerivation rec {
···5962 installPhase = lib.optionalString watchOnly ''
6063 install -m 0755 -D watch $out/bin/watch
6164 install -m 0644 -D watch.1 $out/share/man/man1/watch.1
6565+ '';
6666+6767+ # no obvious exec in documented arguments; haven't trawled source
6868+ # to figure out what exec binlore hits on
6969+ passthru.binlore.out = binlore.synthesize procps ''
7070+ execer cannot bin/{ps,top,free}
6271 '';
63726473 meta = with lib; {
+23-1
pkgs/tools/misc/coreutils/default.nix
···77, perl
88, texinfo
99, xz
1010+, binlore
1111+, coreutils
1012, gmpSupport ? true, gmp
1113, aclSupport ? stdenv.isLinux, acl
1214, attrSupport ? stdenv.isLinux, attr
···2729assert selinuxSupport -> libselinux != null && libsepol != null;
28302931let
3030- inherit (lib) concatStringsSep isString optional optionals optionalString;
3232+ inherit (lib) concatStringsSep isString optional optionalAttrs optionals optionalString;
3133 isCross = (stdenv.hostPlatform != stdenv.buildPlatform);
3234in
3335stdenv.mkDerivation rec {
···180182 + optionalString minimal ''
181183 rm -r "$out/share"
182184 '';
185185+186186+ passthru = {} // optionalAttrs (singleBinary != false) {
187187+ # everything in the single binary gets the same verdict, so we
188188+ # override _that case_ with verdicts from separate binaries.
189189+ #
190190+ # binlore only spots exec in runcon on some platforms (i.e., not
191191+ # darwin; see comment on inverse case below)
192192+ binlore.out = binlore.synthesize coreutils ''
193193+ execer can bin/{chroot,env,install,nice,nohup,runcon,sort,split,stdbuf,timeout}
194194+ execer cannot bin/{[,b2sum,base32,base64,basename,basenc,cat,chcon,chgrp,chmod,chown,cksum,comm,cp,csplit,cut,date,dd,df,dir,dircolors,dirname,du,echo,expand,expr,factor,false,fmt,fold,groups,head,hostid,id,join,kill,link,ln,logname,ls,md5sum,mkdir,mkfifo,mknod,mktemp,mv,nl,nproc,numfmt,od,paste,pathchk,pinky,pr,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,seq,sha1sum,sha224sum,sha256sum,sha384sum,sha512sum,shred,shuf,sleep,stat,stty,sum,sync,tac,tail,tee,test,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,vdir,wc,who,whoami,yes}
195195+ '';
196196+ } // optionalAttrs (singleBinary == false) {
197197+ # binlore only spots exec in runcon on some platforms (i.e., not
198198+ # darwin; I have a note that the behavior may need selinux?).
199199+ # hard-set it so people working on macOS don't miss cases of
200200+ # runcon until ofBorg fails.
201201+ binlore.out = binlore.synthesize coreutils ''
202202+ execer can bin/runcon
203203+ '';
204204+ };
183205184206 meta = with lib; {
185207 homepage = "https://www.gnu.org/software/coreutils/";
···11-{ lib, stdenv, fetchFromGitHub, asciidoctor, gawk, gnused, runtimeShell }:
11+{ lib, stdenv, fetchFromGitHub, asciidoctor, gawk, gnused, runtimeShell, binlore, esh }:
2233stdenv.mkDerivation rec {
44 pname = "esh";
···29293030 doCheck = true;
3131 checkTarget = "test";
3232+3333+ # working around a bug in file. Was fixed in
3434+ # file 5.41-5.43 but regressed in 5.44+
3535+ # see https://bugs.astron.com/view.php?id=276
3636+ # "can" verdict because of `-s SHELL` arg
3737+ passthru.binlore.out = binlore.synthesize esh ''
3838+ execer can bin/esh
3939+ '';
32403341 meta = with lib; {
3442 description = "Simple templating engine based on shell";
+33-2
pkgs/top-level/unixtools.nix
···11-{ pkgs, buildEnv, runCommand, lib, stdenv, freebsd }:
11+{ pkgs, buildEnv, runCommand, lib, stdenv, freebsd, binlore }:
2233# These are some unix tools that are commonly included in the /usr/bin
44# and /usr/sbin directory under more normal distributions. Along with
···3030 priority = 10;
3131 platforms = platforms.${stdenv.hostPlatform.parsed.kernel.name} or platforms.all;
3232 };
3333- passthru = { inherit provider; };
3333+ passthru = { inherit provider; } // lib.optionalAttrs (builtins.hasAttr "binlore" providers) {
3434+ binlore.out = (binlore.synthesize (getBin bins.${cmd}) providers.binlore);
3535+ };
3436 preferLocalBuild = true;
3537 } ''
3638 if ! [ -x ${bin} ]; then
···7678 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc
7779 else pkgs.netbsd.getconf;
7880 darwin = pkgs.darwin.system_cmds;
8181+ # I don't see any obvious arg exec in the doc/manpage
8282+ binlore = ''
8383+ execer cannot bin/getconf
8484+ '';
7985 };
8086 getent = {
8187 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc.getent
···118124 linux = pkgs.glibc;
119125 darwin = pkgs.darwin.adv_cmds;
120126 freebsd = pkgs.freebsd.locale;
127127+ # technically just targeting glibc version
128128+ # no obvious exec in manpage
129129+ binlore = ''
130130+ execer cannot bin/locale
131131+ '';
121132 };
122133 logger = {
123134 linux = pkgs.util-linux;
···130141 linux = pkgs.util-linux;
131142 darwin = pkgs.darwin.diskdev_cmds;
132143 freebsd = freebsd.mount;
144144+ # technically just targeting the darwin version; binlore already
145145+ # ids the util-linux copy as 'cannot'
146146+ # no obvious exec in manpage args; I think binlore flags 'can'
147147+ # on the code to run `mount_<filesystem>` variants
148148+ binlore = ''
149149+ execer cannot bin/mount
150150+ '';
133151 };
134152 netstat = {
135153 linux = pkgs.nettools;
···145163 linux = pkgs.procps;
146164 darwin = pkgs.darwin.ps;
147165 freebsd = pkgs.freebsd.bin;
166166+ # technically just targeting procps ps (which ids as can)
167167+ # but I don't see obvious exec in args; have yet to look
168168+ # for underlying cause in source
169169+ binlore = ''
170170+ execer cannot bin/ps
171171+ '';
148172 };
149173 quota = {
150174 linux = pkgs.linuxquota;
···168192 linux = pkgs.procps;
169193 darwin = pkgs.darwin.top;
170194 freebsd = pkgs.freebsd.top;
195195+ # technically just targeting procps top; haven't needed this in
196196+ # any scripts so far, but overriding it for consistency with ps
197197+ # override above and in procps. (procps also overrides 'free',
198198+ # but it isn't included here.)
199199+ binlore = ''
200200+ execer cannot bin/top
201201+ '';
171202 };
172203 umount = {
173204 linux = pkgs.util-linux;