···23, cmake
24, nix
25, samba
26+27+# for passthru.lore
28+, binlore
29}:
3031assert xarSupport -> libxml2 != null;
···128 passthru.tests = {
129 inherit cmake nix samba;
130 };
131+132+ # bsdtar is detected as "cannot" because its exec is internal to
133+ # calls it makes into libarchive itself. If binlore gains support
134+ # for detecting another layer down into libraries, this can be cut.
135+ passthru.binlore.out = binlore.synthesize finalAttrs.finalPackage ''
136+ execer can bin/bsdtar
137+ '';
138})
···11, mouseSupport ? false, gpm
12, unicodeSupport ? true
13, testers
14+, binlore
15}:
1617stdenv.mkDerivation (finalAttrs: {
···179180 preFixup = lib.optionalString (!stdenv.hostPlatform.isCygwin && !enableStatic) ''
181 rm "$out"/lib/*.a
182+ '';
183+184+ # I'm not very familiar with ncurses, but it looks like most of the
185+ # exec here will run hard-coded executables. There's one that is
186+ # dynamic, but it looks like it only comes from executing a terminfo
187+ # file, so I think it isn't going to be under user control via CLI?
188+ # Happy to have someone help nail this down in either direction!
189+ # The "capability" is 'iprog', and I could only find 1 real example:
190+ # https://invisible-island.net/ncurses/terminfo.ti.html#tic-linux-s
191+ passthru.binlore.out = binlore.synthesize ncurses ''
192+ execer cannot bin/{reset,tput,tset}
193 '';
194195 meta = with lib; {
···56 # in here, but I'm erring on the side of flexibility
57 # since this form will make it easier to pilot other
58 # uses of binlore.
59- callback = lore: drv: overrides: ''
60 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
61 echo generating binlore for $drv by running:
62 echo "${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}"
63 else
64 echo "failed to generate binlore for $drv (none of ${drv}/{bin,lib,libexec} exist)"
65 fi
66- '' +
67- /*
68- Override lore for some packages. Unsure, but for now:
69- 1. start with the ~name (pname-version)
70- 2. remove characters from the end until we find a match
71- in overrides/
72- 3. execute the override script with the list of expected
73- lore types
74- */
75- ''
76- i=''${#identifier}
77- filter=
78- while [[ $i > 0 ]] && [[ -z "$filter" ]]; do
79- if [[ -f "${overrides}/''${identifier:0:$i}" ]]; then
80- filter="${overrides}/''${identifier:0:$i}"
81- echo using "${overrides}/''${identifier:0:$i}" to generate overriden binlore for $drv
82- break
83- fi
84- ((i--)) || true # don't break build
85- done # || true # don't break build
86 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
87- ${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback} "$filter"
88 fi
89 '';
90 };
91- overrides = (src + "/overrides");
9293in rec {
00000000000094 collect = { lore ? loreDef, drvs, strip ? [ ] }: (runCommand "more-binlore" { } ''
95 mkdir $out
96 for lorefile in ${toString lore.types}; do
97 cat ${lib.concatMapStrings (x: x + "/$lorefile ") (map (make lore) (map lib.getBin (builtins.filter lib.isDerivation drvs)))} > $out/$lorefile
98- substituteInPlace $out/$lorefile ${lib.concatMapStrings (x: "--replace '${x}/' '' ") strip}
99 done
100 '');
101- # TODO: echo for debug, can be removed at some point
00000000000000102 make = lore: drv: runCommand "${drv.name}-binlore" {
103- identifier = drv.name;
104 drv = drv;
105 } (''
106 mkdir $out
107 touch $out/{${builtins.concatStringsSep "," lore.types}}
108109- ${lore.callback lore drv overrides}
0000000000000110111 echo binlore for $drv written to $out
112 '');
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000113}
···56 # in here, but I'm erring on the side of flexibility
57 # since this form will make it easier to pilot other
58 # uses of binlore.
59+ callback = lore: drv: ''
60 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
61 echo generating binlore for $drv by running:
62 echo "${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}"
63 else
64 echo "failed to generate binlore for $drv (none of ${drv}/{bin,lib,libexec} exist)"
65 fi
66+000000000000000000067 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
68+ ${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}
69 fi
70 '';
71 };
07273in rec {
74+ /*
75+ Output a directory containing lore for multiple drvs.
76+77+ This will `make` lore for drv in drvs and then combine lore
78+ of the same type across all packages into a single file.
79+80+ When drvs are also specified in the strip argument, corresponding
81+ lore is made relative by stripping the path of each drv from
82+ matching entries. (This is mainly useful in a build process that
83+ uses a chain of two or more derivations where the output of one
84+ is the source for the next. See resholve for an example.)
85+ */
86 collect = { lore ? loreDef, drvs, strip ? [ ] }: (runCommand "more-binlore" { } ''
87 mkdir $out
88 for lorefile in ${toString lore.types}; do
89 cat ${lib.concatMapStrings (x: x + "/$lorefile ") (map (make lore) (map lib.getBin (builtins.filter lib.isDerivation drvs)))} > $out/$lorefile
90+ substituteInPlace $out/$lorefile ${lib.concatMapStrings (x: "--replace-quiet '${x}/' '' ") strip}
91 done
92 '');
93+94+ /*
95+ Output a directory containing lore for a single drv.
96+97+ This produces lore for the derivation (via lore.callback) and
98+ appends any lore that the derivation itself wrote to nix-support
99+ or which was overridden in drv.binlore.<outputName> (passthru).
100+101+ > *Note*: Since the passthru is attached to all outputs, binlore
102+ > is an attrset namespaced by outputName to support packages with
103+ > executables in more than one output.
104+105+ Since the last entry wins, the effective priority is:
106+ drv.binlore.<outputName> > $drv/nix-support > lore generated here by callback
107+ */
108 make = lore: drv: runCommand "${drv.name}-binlore" {
0109 drv = drv;
110 } (''
111 mkdir $out
112 touch $out/{${builtins.concatStringsSep "," lore.types}}
113114+ ${lore.callback lore drv}
115+ '' +
116+ # append lore from package's $out and drv.binlore.${drv.outputName} (last entry wins)
117+ ''
118+ for lore_type in ${builtins.toString lore.types}; do
119+ if [[ -f "${drv}/nix-support/$lore_type" ]]; then
120+ cat "${drv}/nix-support/$lore_type" >> "$out/$lore_type"
121+ fi
122+ '' + lib.optionalString (builtins.hasAttr "binlore" drv && builtins.hasAttr drv.outputName drv.binlore) ''
123+ if [[ -f "${drv.binlore."${drv.outputName}"}/$lore_type" ]]; then
124+ cat "${drv.binlore."${drv.outputName}"}/$lore_type" >> "$out/$lore_type"
125+ fi
126+ '' + ''
127+ done
128129 echo binlore for $drv written to $out
130 '');
131+132+ /*
133+ Utility function for creating override lore for drv.
134+135+ We normally attach this lore to `drv.passthru.binlore.<outputName>`.
136+137+ > *Notes*:
138+ > - Since the passthru is attached to all outputs, binlore is an
139+ > attrset namespaced by outputName to support packages with
140+ > executables in more than one output. You'll generally just use
141+ > `out` or `bin`.
142+ > - We can reconsider the passthru attr name if someone adds
143+ > a new lore provider. We settled on `.binlore` for now to make it
144+ > easier for people to figure out what this is for.
145+146+ The lore argument should be a Shell script (string) that generates
147+ the necessary lore. You can use arbitrary Shell, but this function
148+ includes a shell DSL you can use to declare/generate lore in most
149+ cases. It has the following functions:
150+151+ - `execer <verdict> [<path>...]`
152+ - `wrapper <wrapper_path> <original_path>`
153+154+ Writing every override explicitly in a Nix list would be tedious
155+ for large packages, but this small shell DSL enables us to express
156+ many overrides efficiently via pathname expansion/globbing.
157+158+ Here's a very general example of both functions:
159+160+ passthru.binlore.out = binlore.synthesize finalAttrs.finalPackage ''
161+ execer can bin/hello bin/{a,b,c}
162+ wrapper bin/hello bin/.hello-wrapped
163+ '';
164+165+ And here's a specific example of how pathname expansion enables us
166+ to express lore for the single-binary variant of coreutils while
167+ being both explicit and (somewhat) efficient:
168+169+ passthru = {} // optionalAttrs (singleBinary != false) {
170+ binlore.out = binlore.synthesize coreutils ''
171+ execer can bin/{chroot,env,install,nice,nohup,runcon,sort,split,stdbuf,timeout}
172+ 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}
173+ '';
174+ };
175+176+ Caution: Be thoughtful about using a bare wildcard (*) glob here.
177+ We should generally override lore only when a human understands if
178+ the executable will exec arbitrary user-passed executables. A bare
179+ glob can match new executables added in future package versions
180+ before anyone can audit them.
181+ */
182+ synthesize = drv: loreSynthesizingScript: runCommand "${drv.name}-lore-override" {
183+ drv = drv;
184+ } (''
185+ execer(){
186+ local verdict="$1"
187+188+ shift
189+190+ for path in "$@"; do
191+ if [[ -f "$PWD/$path" ]]; then
192+ echo "$verdict:$PWD/$path"
193+ else
194+ echo "error: Tried to synthesize execer lore for missing file: $PWD/$path" >&2
195+ exit 2
196+ fi
197+ done
198+ } >> $out/execers
199+200+ wrapper(){
201+ local wrapper="$1"
202+ local original="$2"
203+204+ if [[ ! -f "$wrapper" ]]; then
205+ echo "error: Tried to synthesize wrapper lore for missing wrapper: $PWD/$wrapper" >&2
206+ exit 2
207+ fi
208+209+ if [[ ! -f "$original" ]]; then
210+ echo "error: Tried to synthesize wrapper lore for missing original: $PWD/$original" >&2
211+ exit 2
212+ fi
213+214+ echo "$PWD/$wrapper:$PWD/$original"
215+216+ } >> $out/wrappers
217+218+ mkdir $out
219+220+ # lore override commands are relative to the drv root
221+ cd $drv
222+223+ '' + loreSynthesizingScript);
224}
+9
pkgs/os-specific/linux/nixos-rebuild/default.nix
···10, lib
11, nixosTests
12, installShellFiles
0013}:
14let
15 fallback = import ./../../../../nixos/modules/installer/tools/nix-fallback-paths.nix;
···48 specialisations = nixosTests.nixos-rebuild-specialisations;
49 target-host = nixosTests.nixos-rebuild-target-host;
50 };
00000005152 meta = {
53 description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote";
···10, lib
11, nixosTests
12, installShellFiles
13+, binlore
14+, nixos-rebuild
15}:
16let
17 fallback = import ./../../../../nixos/modules/installer/tools/nix-fallback-paths.nix;
···50 specialisations = nixosTests.nixos-rebuild-specialisations;
51 target-host = nixosTests.nixos-rebuild-target-host;
52 };
53+54+ # nixos-rebuild can’t execute its arguments
55+ # (but it can run ssh with the with the options stored in $NIX_SSHOPTS,
56+ # and ssh can execute its arguments...)
57+ passthru.binlore.out = binlore.synthesize nixos-rebuild ''
58+ execer cannot bin/nixos-rebuild
59+ '';
6061 meta = {
62 description = "Rebuild your NixOS configuration and switch to it, on local hosts and remote";
+9
pkgs/os-specific/linux/procps-ng/default.nix
···15 # exception is ‘watch’ which is portable enough to run on pretty much
16 # any UNIX-compatible system.
17, watchOnly ? !(stdenv.isLinux || stdenv.isCygwin)
00018}:
1920stdenv.mkDerivation rec {
···59 installPhase = lib.optionalString watchOnly ''
60 install -m 0755 -D watch $out/bin/watch
61 install -m 0644 -D watch.1 $out/share/man/man1/watch.1
00000062 '';
6364 meta = with lib; {
···15 # exception is ‘watch’ which is portable enough to run on pretty much
16 # any UNIX-compatible system.
17, watchOnly ? !(stdenv.isLinux || stdenv.isCygwin)
18+19+, binlore
20+, procps
21}:
2223stdenv.mkDerivation rec {
···62 installPhase = lib.optionalString watchOnly ''
63 install -m 0755 -D watch $out/bin/watch
64 install -m 0644 -D watch.1 $out/share/man/man1/watch.1
65+ '';
66+67+ # no obvious exec in documented arguments; haven't trawled source
68+ # to figure out what exec binlore hits on
69+ passthru.binlore.out = binlore.synthesize procps ''
70+ execer cannot bin/{ps,top,free}
71 '';
7273 meta = with lib; {
···1-{ lib, stdenv, fetchFromGitHub, asciidoctor, gawk, gnused, runtimeShell }:
23stdenv.mkDerivation rec {
4 pname = "esh";
···2930 doCheck = true;
31 checkTarget = "test";
000000003233 meta = with lib; {
34 description = "Simple templating engine based on shell";
···1+{ lib, stdenv, fetchFromGitHub, asciidoctor, gawk, gnused, runtimeShell, binlore, esh }:
23stdenv.mkDerivation rec {
4 pname = "esh";
···2930 doCheck = true;
31 checkTarget = "test";
32+33+ # working around a bug in file. Was fixed in
34+ # file 5.41-5.43 but regressed in 5.44+
35+ # see https://bugs.astron.com/view.php?id=276
36+ # "can" verdict because of `-s SHELL` arg
37+ passthru.binlore.out = binlore.synthesize esh ''
38+ execer can bin/esh
39+ '';
4041 meta = with lib; {
42 description = "Simple templating engine based on shell";
+33-2
pkgs/top-level/unixtools.nix
···1-{ pkgs, buildEnv, runCommand, lib, stdenv, freebsd }:
23# These are some unix tools that are commonly included in the /usr/bin
4# and /usr/sbin directory under more normal distributions. Along with
···30 priority = 10;
31 platforms = platforms.${stdenv.hostPlatform.parsed.kernel.name} or platforms.all;
32 };
33- passthru = { inherit provider; };
0034 preferLocalBuild = true;
35 } ''
36 if ! [ -x ${bin} ]; then
···76 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc
77 else pkgs.netbsd.getconf;
78 darwin = pkgs.darwin.system_cmds;
000079 };
80 getent = {
81 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc.getent
···118 linux = pkgs.glibc;
119 darwin = pkgs.darwin.adv_cmds;
120 freebsd = pkgs.freebsd.locale;
00000121 };
122 logger = {
123 linux = pkgs.util-linux;
···130 linux = pkgs.util-linux;
131 darwin = pkgs.darwin.diskdev_cmds;
132 freebsd = freebsd.mount;
0000000133 };
134 netstat = {
135 linux = pkgs.nettools;
···145 linux = pkgs.procps;
146 darwin = pkgs.darwin.ps;
147 freebsd = pkgs.freebsd.bin;
000000148 };
149 quota = {
150 linux = pkgs.linuxquota;
···168 linux = pkgs.procps;
169 darwin = pkgs.darwin.top;
170 freebsd = pkgs.freebsd.top;
0000000171 };
172 umount = {
173 linux = pkgs.util-linux;
···1+{ pkgs, buildEnv, runCommand, lib, stdenv, freebsd, binlore }:
23# These are some unix tools that are commonly included in the /usr/bin
4# and /usr/sbin directory under more normal distributions. Along with
···30 priority = 10;
31 platforms = platforms.${stdenv.hostPlatform.parsed.kernel.name} or platforms.all;
32 };
33+ passthru = { inherit provider; } // lib.optionalAttrs (builtins.hasAttr "binlore" providers) {
34+ binlore.out = (binlore.synthesize (getBin bins.${cmd}) providers.binlore);
35+ };
36 preferLocalBuild = true;
37 } ''
38 if ! [ -x ${bin} ]; then
···78 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc
79 else pkgs.netbsd.getconf;
80 darwin = pkgs.darwin.system_cmds;
81+ # I don't see any obvious arg exec in the doc/manpage
82+ binlore = ''
83+ execer cannot bin/getconf
84+ '';
85 };
86 getent = {
87 linux = if stdenv.hostPlatform.libc == "glibc" then pkgs.stdenv.cc.libc.getent
···124 linux = pkgs.glibc;
125 darwin = pkgs.darwin.adv_cmds;
126 freebsd = pkgs.freebsd.locale;
127+ # technically just targeting glibc version
128+ # no obvious exec in manpage
129+ binlore = ''
130+ execer cannot bin/locale
131+ '';
132 };
133 logger = {
134 linux = pkgs.util-linux;
···141 linux = pkgs.util-linux;
142 darwin = pkgs.darwin.diskdev_cmds;
143 freebsd = freebsd.mount;
144+ # technically just targeting the darwin version; binlore already
145+ # ids the util-linux copy as 'cannot'
146+ # no obvious exec in manpage args; I think binlore flags 'can'
147+ # on the code to run `mount_<filesystem>` variants
148+ binlore = ''
149+ execer cannot bin/mount
150+ '';
151 };
152 netstat = {
153 linux = pkgs.nettools;
···163 linux = pkgs.procps;
164 darwin = pkgs.darwin.ps;
165 freebsd = pkgs.freebsd.bin;
166+ # technically just targeting procps ps (which ids as can)
167+ # but I don't see obvious exec in args; have yet to look
168+ # for underlying cause in source
169+ binlore = ''
170+ execer cannot bin/ps
171+ '';
172 };
173 quota = {
174 linux = pkgs.linuxquota;
···192 linux = pkgs.procps;
193 darwin = pkgs.darwin.top;
194 freebsd = pkgs.freebsd.top;
195+ # technically just targeting procps top; haven't needed this in
196+ # any scripts so far, but overriding it for consistency with ps
197+ # override above and in procps. (procps also overrides 'free',
198+ # but it isn't included here.)
199+ binlore = ''
200+ execer cannot bin/top
201+ '';
202 };
203 umount = {
204 linux = pkgs.util-linux;