CARE(1)
β¨ NAME β¨
okay so CARE(1) stands for Comprehensive Archiver for Reproducible Execution π yes she has a whole government name and she earned every word of it, no cap
πΈ SYNOPSIS πΈ
care [option] ... command
π¦ DESCRIPTION π¦
So here's the vibe: care watches your command do its thing and builds an archive that has literally everything needed to run it again in the exact same context. Which means your command becomes reproducible everywhere β even on Linux systems that are supposed to be, like, completely incompatible with the original Linux system π«Ά
CARE is the moment for getting reliable bug reports, for demonstrations, for artifact evaluation (yes, http://www.artifact-eval.org, bestie, that's a whole thing), for tutorials, for portable applications, for a minimal rootfs, for file-system coverage, and honestly justβ¦ more β¨
Here's the part that's actually kind of genius: by design, CARE does not record events at all. Not one. Instead it archives environment variables and accessed file-system components β before modification β during the so-called initial execution. Then, to reproduce that execution, the re-execute.sh script embedded into the archive restores the environment variables and relaunches the command, confined into the saved file-system. So both the initial and the reproduced executions should produce the same results, because they're using the same context π
β¦assuming they don't rely on external events β like key strokes or network packets β or that those external events get replayed manually or automatically, using umockdev for instance (https://github.com/martinpitt/umockdev, iykyk).
And the fun part? That means you can deliberately alter the reproduced executions, by changing content of the saved file-system, or by replaying different external events. Go off, honestly π
π Privacy π
okay this one is important, listen up.
To ensure that no sensitive file can possibly leak into the archive, CARE conceals recursively the content of $HOME and /tmp β like, they appear empty during the original execution π
Although, for consistency reasons, the content of $PWD is revealed, even if it's nested into those two previous paths. Yeah. She said what she said.
As a consequence, a program executed under CARE may behave unexpectedly, because a required path is not accessible anymore π If that happens, that path has to be revealed explicitly. For details, go look at the options --concealed-path and --revealed-path, and the file concealed-accesses.txt as well.
π Real talk: it is advised to inspect the archived content before sharing it. Every single time. No exceptions.
π OPTIONS π
The command-line interface is composed of two parts: first CARE's options, then the command to launch. This section describes the options supported by CARE β that is, the first part of its command-line interface. The second part is, you know, your actual command π
-opath,--output=path β Archive in path, its suffix specifies the format.
The suffix of path is used to select the archive format, and it can be one of the following:
/β don't archive, copy into the specified directory instead.tarβ most common archive format.cpioβ most portable archive format, it can archive sockets too?.gzβ most common compression format, but slow?.lzoβ fast compression format, but uncommon?.binβ see Self-extracting format section?.?.binβ see Self-extracting format section.binβ see Self-extracting format section.rawβ recommended archive format, usecare -xto extract
where ? means the suffix must be combined with another one. For examples: .tar.lzo, .cpio.gz, .tar.bin, .cpio.lzo.bin, ... β¨
If this option is not specified, the default output path is care-<DATE>.bin or care-<DATE>.raw, depending on whether CARE was built with self-extracting format support or not. So yeah, the default kinda depends on how she was raised π
-cpath,--concealed-path=path β Make path content appear empty during the original execution.
Some paths may contain sensitive data that should never be archived. This is typically the case for most of the files in:
$HOME/tmp
That's why these directories are recursively concealed from the original execution, unless the -d option is specified. Concealed paths appear empty during the original execution β as a consequence their original content can't be accessed nor archived π«£
-rpath,--revealed-path=path β Make path content accessible when nested in a concealed path.
Concealed paths might make the original execution with CARE behave differently from an execution without CARE. For example, a lot of No such file or directory errors might appear π The solution is to reveal recursively any required paths that would be nested into a concealed path. Note that $PWD is revealed, unless the -d option is specified π
-ppath,--volatile-path=path β Don't archive path content, reuse actual path instead.
Some paths contain only communication means with programs that can't be monitored by CARE β like the kernel, or a remote server. Such paths are said volatile; they shouldn't be archived, instead they must be accessed from the actual rootfs during the re-execution. This is typically the case for the following pseudo file-systems, sockets, and authority files:
/dev/proc/sys/run/shm/tmp/.X11-unix/tmp/.ICE-unix$XAUTHORITY$ICEAUTHORITY/var/run/dbus/system_bus_socket/var/tmp/kdecache-$LOGNAME
This is also typically the case for any other fifos or sockets. These paths are considered volatile, unless the -d option is specified π
-ename,--volatile-env=name β Don't archive name env. variable, reuse actual value instead.
Some environment variables are used to communicate with programs that can't be monitored by CARE β like remote servers. Such environment variables are said volatile; they shouldn't be archived, instead they must be accessed from the actual environment during the re-execution. This is typically the case for the following ones:
DISPLAYhttp_proxyhttps_proxyftp_proxyall_proxyHTTP_PROXYHTTPS_PROXYFTP_PROXYALL_PROXYDBUS_SESSION_BUS_ADDRESSSESSION_MANAGERXDG_SESSION_COOKIE
These environment variables are considered volatile, unless the -d option is specified π
-mvalue,--max-archivable-size=value β Set the maximum size of archivable files to value megabytes.
To keep the CPU time and the disk space used by the archiver reasonable, files whose size exceeds value megabytes are truncated down to 0 bytes π³ The default is 1GB, unless the -d option is specified. A negative value means no limit β go wild, bestie.
-d,--ignore-default-configβ Don't use the default options.
That's it. That's the whole thing. All those "unless the -d option is specified" clauses above? This is the girl they were talking about π
-xfile,--extract=file β Extract content of the archive file, then exit.
It is recommended to use this option to extract archives created by CARE, because most extracting tools β that are not based on libarchive β are too limited to extract them correctly π«Ά
-vvalue,--verbose=value β Set the level of debug information to value.
The higher the integer value is, the more detailed debug information is printed to the standard error stream. A negative value makes CARE quiet except on fatal errors π€«
-V,--version,--aboutβ Print version, copyright, license and contact, then exit.-h,--help,--usageβ Print the user manual, then exit.
π EXIT STATUS π
If an internal error occurs, care returns a non-zero exit status. Otherwise it returns the exit status of the last terminated program.
β οΈ When an error has occurred, the only way to know if it comes from the last terminated program or from care itself is to have a look at the error message. Yes, only way. Read the message, bestie, it's literally right there π
π FILES π
bestie the output archive is like a little time capsule and it comes with these files inside, ok? here's the whole roster:
πΈ re-execute.sh
this one starts the re-execution of the initial command as originally specified. and the cute part? you can also feed it an alternate command if you wanna switch things up.
so for example, let's say gcc got archived β you can re-invoke it completely differently:
$ ./re-execute.sh gcc --version gcc (Ubuntu/Linaro
4.5.2-8ubuntu4) 4.5.2
$ echo 'int main(void) { return puts("OK"); }' > rootfs/foo.c
$ ./re-execute.sh gcc -Wall /foo.c $ foo.c: In function
"main": $ foo.c:1:1: warning: implicit declaration of function
"puts"
π rootfs/
the directory where all the files used during the original execution were archived. these are literally required for the reproduced execution, so don't go deleting them, bestie, i'm serious π«Ά
π proot
the virtualization tool that re-execute.sh invokes to confine the reproduced execution inside the rootfs. it also emulates the missing kernel features if needed. icon behaviour fr fr.
π¦ concealed-accesses.txt
the list of accessed paths that were concealed during the original execution. its whole purpose is so you know which paths should be revealed if the original execution didn't go as expected.
heads up: it is absolutely useless for the reproduced execution. like, zero use. it's just the receipts β¨
π LIMITATIONS π
okay so this is the part where i have to be real with you:
it is not possible to use GDB, strace, or any programs based on ptrace under CARE yet.
why tho? because CARE itself is also based on this syscall, and the Linux kernel allows only one ptracer per process. it's giving "one (1) babysitter per child" energy π
this will be fixed in a future version of CARE thanks to a ptrace emulator. so manifesting, but for now that's the vibe.
β¨ EXAMPLE β¨
okay this one's a whole story and i'm obsessed, so buckle up π
in this example, Alice wants to report to Bob that the compilation of PRoot v2.4 raises an unexpected warning:
alice$ make -C PRoot-2.4/src/
make: Entering directory `PRoot-2.4/src'
[...]
CC path/proc.o
./path/proc.c: In function 'readlink_proc':
./path/proc.c:132:3: warning: ignoring return value of 'strtol'
[...]
technically, Alice uses Ubuntu 11.04 for x86, whereas Bob uses Slackware 13.37 on x86_64. both distros are supposed to ship with GCC 4.5.2, but Bob cannot reproduce this issue on his system:
bob$ make -C PRoot-2.4/src/
make: Entering directory `PRoot-2.4/src'
[...]
CC path/proc.o
[...]
since they don't have much time to investigate by iterating back and forth between each other, they decide to use CARE. first, Alice prepends care to her command:
alice$ care make -C PRoot-2.4/src/
care info: concealed path: $HOME
care info: concealed path: /tmp
care info: revealed path: $PWD
care info: ----------------------------------------------------------------------
make: Entering directory `PRoot-2.4/src'
[...]
CC path/proc.o
./path/proc.c: In function 'readlink_proc':
./path/proc.c:132:3: warning: ignoring return value of 'strtol'
[...]
care info: ----------------------------------------------------------------------
care info: Hints:
care info: - search for "conceal" in `care -h` if the execution didn't go as expected.
care info: - use `./care-130213072430.bin` to extract the output archive.
then she sends the care-130213072430.bin file to Bob. now he should be able to reproduce her issue on his system:
bob$ ./care-130213072430.bin
[...]
bob$ ./care-130213072430/re-execute.sh
make: Entering directory `PRoot-2.4/src'
[...]
CC path/proc.o
./path/proc.c: In function 'readlink_proc':
./path/proc.c:132:3: warning: ignoring return value of 'strtol'
[...]
so far so good! but this compiler warning doesn't make sense to Bob, because strtol is used there just to check a string format β the return value is useless, only the errno value matters. further investigations are required, so Bob re-executes Alice's GCC differently to get more details:
bob$ ./care-130213072430/re-execute.sh gcc --version
gcc (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
the same invocation on his own system returns something slightly different:
bob$ gcc --version
gcc (GCC) 4.5.2
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
this confirms both GCC versions are the same, but Alice's one seems to have been modified by Ubuntu. although, according to the web page related to this Ubuntu package [1], no changes regarding strtol were made. so Bob decides to search into the files coming from Alice's system β that is, the rootfs directory in the archive:
bob$ grep -wIrl strtol ./care-130213072430/rootfs
care-130213072430/rootfs/usr/include/inttypes.h
care-130213072430/rootfs/usr/include/stdlib.h
[...]
here, the file usr/include/stdlib.h contains a declaration of strtol with the "warn unused result" attribute. on Ubuntu, this file belongs to the EGLIBC package, and its related web page [2] shows that this attribute was actually wrongly introduced by the official EGLIBC developers. ultimately Bob should notify them in this regard.
thanks to CARE, Bob was able to reproduce the issue reported by Alice without effort. for investigation purposes, he was able to re-execute programs differently and to search into the relevant files. main character energy honestly π
[1] <https://launchpad.net/ubuntu/oneiric/+source/gcc-4.5/4.5.2-8ubuntu4>
[2] <https://launchpad.net/ubuntu/+source/eglibc/2.13-0ubuntu13.2>
π§ SELF-EXTRACTING FORMAT π§
the self-extracting format used by CARE starts with an extracting program, followed by a regular archive, and it ends with a special footer. this footer contains the signature "I_LOVE_PIZZA" followed by the size of the embedded archive:
+------------------------+
| extracting program |
+------------------------+
| |
| embedded archive |
| |
+------------------------+
| uint8_t signature[13] |
| uint64_t archive_size | # big-endian
+------------------------+
the command care -x can be used against a self-extracting archive, even if they were not built for the same architecture. for instance, a self-extracting archive produced for ARM can be extracted with a care program built for x86_64, and vice versa. it's also possible to use external tools to extract the embedded archive, for example:
$ care -o foo.tar.gz.bin /usr/bin/echo OK
[...]
OK
[...]
$ hexdump -C foo.tar.gz.bin | tail -3
0015b5b0 00 b0 2e 00 49 5f 4c 4f 56 45 5f 50 49 5a 5a 41 |....I_LOVE_PIZZA|
0015b5c0 00 00 00 00 00 00 12 b4 13 |.........|
0015b5c9
$ file_size=`stat -c %s foo.tar.gz.bin`
$ archive_size=$((16#12b413))
$ footer_size=21
$ skip=$(($file_size - $archive_size - $footer_size))
$ dd if=foo.tar.gz.bin of=foo.tar.gz bs=1 skip=$skip count=$archive_size
1225747+0 records in
1225747+0 records out
1225747 bytes (1.2 MB) copied, 2.99546 s, 409 kB/s
$ file foo.tar.gz
foo.tar.gz: gzip compressed data, from Unix
$ tar -tzf foo.tar.gz
foo/rootfs/usr/
[...]
foo/re-execute.sh
foo/README.txt
foo/proot
π DOWNLOADS π
CARE is heavily based on PRoot <https://proot-me.github.io>, that's why they're both hosted in the same repository: <https://github.com/proot-me/proot>.
previous CARE releases were packaged at <https://github.com/proot-me/proot-static-build/releases>, however, that repository has since been archived.
the latest builds can be found under the job artifacts for the GitLab CI/CD Pipelines <https://gitlab.com/proot/proot/pipelines> for each commit. no cap, that's where they live now π«Ά
π COLOPHON π
visit <https://proot-me.github.io/care> for help, bug reports, suggestions, patches, ...
Copyright (C) 2023 PRoot Developers, licensed under GPL v2 or later.
_____ ____ _____ ____
/ __/ __ | __ \ __|
/ /_/ | / __|
\_____|__|__|__|__\____|
and that's the whole page, bestie!! π this was CARE version 2.3.0, dated 2023-05-13, and yes the man page itself is filed under CARE(1) π
go forth and reproduce some executions, you've got this fr fr β¨π¦
π¦ where this page comes from
- ubuntu resolute: care 5.4.0-3 Β· GPL-2.0-or-later
- ubuntu stonking: care 5.4.0-3 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms