7

If I have a lot of commands I've issued and I want the exit status of a process that exited say 100 commands ago. Is there a struct, file location, or variable in linux I can access all of exited processes and see information about them?

3

If BSD process accounting was enabled (accton on was issued), with GNU acct 6.6.3 or above, you can get that information from lastcomm --debug or dump-acct /var/log/account/pact (or wherever the process accounting data is stored on your system).

$ perl -e 'exit 123'
$ lastcomm --debug | grep perl
CURRENT REC: perl            |v3|     0.00|     0.00|     0.00|  1000|  1000| 26328.00|     0.00|     332|    8530|     |     123|pts/1   |Wed Sep 19 20:21:26 2018
$ dump-acct /var/log/account/pacct | grep perl
perl            |v3|     0.00|     0.00|     0.00|  1000|  1000| 26328.00|     0.00|     332|    8530|     |     123|pts/1   |Wed Sep 19 20:21:26 2018

You get the exit code in the 3rd last field and whether it was killed or not (but not the signal number, see @mosvy's answer for that) in the 4th last one.

2

If you're on debian, you can install the acct package in order to enable process accounting, but notice that neither lastcomm --debug nor dump-acct show anything like the exit status or the signal that terminated a process.

If order to get that data, you can use a script like this:

$ cat pacct.pl
#! /usr/bin/perl
use strict;
use Config;
printf "%-7s %6s %6s  %8s %8s  %s\n",
        'STATUS', 'UID', 'PID', 'BTIME', 'ETIME', 'COMMAND';
my @sig = split ' ', $Config{sig_name};
$/ = \64;
while(<>){
        my @f = unpack 'CCSL6fS8A*', $_;
        my ($flag, $version, $tty, $exitcode, $uid, $gid, $pid, $ppid,
                $btime, $etime, $utime, $stime, $mem, $io, $rw,
                $minflt, $majflt, $swaps, $cmd) = @f;
        my $s = $exitcode & 0x7f;
        my $status = $s ?  "SIG$sig[$s]" : $exitcode >> 8;
        printf "%-7s %6d %6d  %02d:%02d:%02d %8.2f  %-16s\n",
                $status, $uid, $pid,
                (localtime $btime)[2,1,0],
                $etime / 100,
                $cmd;
}

# perl pacct.pl /var/log/account/pacct
# tail -f /var/log/account/pacct | perl pacct.pl

This assumes the version 3 of the log file format -- see acct.h.

Notice however that this isn't that useful, because only the process/thread name is included in the log file (ie the basename of the executable, truncated to 15 bytes, and which could be easily faked with prctl(PR_SET_NAME)), not the path of the executable or the arguments it was invoked with.

If you want to extend that script to also display the stime, utime, etc fields, this may be useful:

# translate comp_t to float
# utime, stime, mem, minflt, majflt are in the comp_t format
# io, rw, swaps are never set; they're purely decorative
sub comp2f {
        my $m = $_[0] & 0x1fff; my $e = $_[0] >> 13; $m * 8 ** $e;
}

Note: Instead of installing the acct package, you can also turn process accounting on with:

# mkdir /var/log/account
# perl -e 'require "syscall.ph"; my $f = shift;
  open my $h, ">>", $f or die "open >>$f: $!" if $f;
  $! = -syscall SYS_acct(), $f // 0 and die "acct $f: $!";
' /var/log/account/pacct
  • What version of Debian did you try lastcomm --debug/dump-acct on? It worked for me on Debian unstable and on Ubuntu 18.04 (though doesn't give the signal number for those processes that were killed) – Stéphane Chazelas Sep 20 '18 at 10:41
  • Note that it's not the command name, it's the process name (changed by each execve() call the process makes to the first 15 bytes of the basename of the executed file). You can't change it with overwriting argv[0], but with prctl(PR_SET_NAME, newname) or execve(). When you do env sh -c 'exec bash -c "exit 123"', that's one process that runs in turn env, sh and bash. The pacct log will show only one entry for bash (the name of the process at the time it exited) – Stéphane Chazelas Sep 20 '18 at 11:01
  • @StéphaneChazelas stretch(stable) – mosvy Sep 20 '18 at 11:16
  • Yes, you need GNU acct 6.6.3 or above. stretch has 6.6.2. – Stéphane Chazelas Sep 20 '18 at 12:20
-1

The

  set -o pipefail 

is pretty close to what you want; it will

A pipeline will not complete until all components of the pipeline have completed, and the return value will be the value of the last non-zero command to fail or zero if no command has failed.

So if you have 40 commands piped together, and the third command gives a return code of 8, and the rest complete successfully, then the overall return code would be 8. To figure out which command gave the bad return code would be tricky.

  • 1
    There is no indication that the commands are issued as part of the same pipeline. – Kusalananda Sep 19 '18 at 19:18
  • Good point; I didn't read the question closely enough, but I'll leave the answer just in case someone may benefit from it. It was one of my nice surprises I found when moving from Solaris to Linux. – Mark Stewart Sep 19 '18 at 20:04
  • 1
    Yeah thanks. There's a PIPESTATUS[0] environmental variable as well for pipelines. This would be just in general. – mutant_city Sep 21 '18 at 1:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.