ps: full command is too long
On Linux, with the ps
from procps(-ng)
:
ps -fwwp 2755
In Linux versions prior to 4.2, it's still limited though (by the kernel (/proc/2755/cmdline
) to 4k) and you can't get more except by asking the process to tell it to you or use a debugger.
$ sh -c 'sleep 1000' $(seq 4000) &
[1] 31149
$ gdb -p $! /bin/sh
[...]
Attaching to program: /bin/dash, process 31149
[...]
(gdb) bt
#0 0x00007f40d11f40aa in wait4 () at ../sysdeps/unix/syscall-template.S:81
[...]
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
#8 0x00000000004024a5 in ?? ()
#9 0x00007fff5b9f5a78 in ?? ()
#10 0x0000000000000000 in ?? ()
(gdb) frame 7
#7 0x00007f40d115c995 in __libc_start_main (main=0x4022c0, argc=4003, ubp_av=0x7fff5b9f5a88, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff5b9f5a78)
at libc-start.c:260
(gdb) x/4003s *ubp_av
0x7fff5b9ff83e: "sh"
0x7fff5b9ff841: "-c"
0x7fff5b9ff844: "sleep 1000"
0x7fff5b9ff84f: "1"
0x7fff5b9ff851: "2"
[...]
0x7fff5ba04212: "3999"
0x7fff5ba04217: "4000"
To print the 4th arg with up to 5000 characters:
(gdb) set print elements 5000
(gdb) p ubp_av[3]
If you want something non-intrusive, you could try and get the information from /proc/2755/mem
(note that if the kernel.yama.ptrace_scope
is not set to 0, you'll need superuser permissions for that). This below works for me (prints all the arguments and environment variables), but there's not much guarantee I would think (the error and unexpected input handling is left as an exercise to the reader):
$ perl -e '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
print((split /\0{2,}/,$c)[-1])' "$!" | tr \\0 \\n | head
sh
-c
sleep 1000
1
2
3
4
5
6
7
(replace "$!"
with the process id). The above uses the fact that Linux puts the strings pointed to by argv[]
, envp[]
and the executed filename at the bottom of the stack of the process.
The above looks in that stack for the bottom-most string in between two sets of two or more consecutive NUL bytes. It doesn't work if any of the arguments or env strings is empty, because then you'll have a sequence of 2 NUL bytes in the middle of those argv or envp. Also, we don't know where the argv strings stop and where the envp ones start.
A work around for that would be to refine that heuristic by looking backwards for the actual content of argv[]
(the pointers). This below works on i386 and amd64 architecture for ELF executables at least:
perl -le '$p=shift;open MAPS, "/proc/$p/maps";
($m)=grep /\[stack\]/, <MAPS>;
($a,$b)=map hex, $m =~ /[\da-f]+/g;
open MEM, "/proc/$p/mem" or die "open mem: $!";
seek MEM,$a,0; read MEM, $c,$b-$a;
$c =~ /.*\0\0\K[^\0].*\0[^\0]*$/s;
@a=unpack"L!*",substr$c,0,$-[0];
for ($i = $#a; $i >=0 && $a[$i] != $a+$-[0];$i--) {}
for ($i--; $i >= 0 && ($a[$i]>$a || $a[$i]==0); $i--) {}
$argc=$a[$i++];
print for unpack"(Z*)$argc",substr$c,$a[$i]-$a;' "$!"
Basically, it does the same as above, but once it has found the first string of argv[]
(or at least one of the argv[]
or envp[]
strings if there are empties), it knows its address, so it looks backward in the top rest of the stack for a pointer with that same value. Then keeps looking backwards until it finds a number that can't be a pointer to those, and that is argc
. Then the next integer is argv[0]
. And knowing argv[0]
and argc
, it can display the list of arguments.
That doesn't work if the process has written to its argv[]
possibly overriding some NUL delimiters or if argc
is 0 (argc
is generally at least 1 to include argv[0]
) but should work in the general case at least for ELF executables.
In 4.2 and newer, /proc/<pid>/cmdline
is no longer truncated, but ps
itself has a maximum display width of 128K.
Add one or two -w
flags. It makes the output wider. e.g. ps auxww
.
In Linux kernel 4.2 and newer, /proc/<pid>/cmdline
is no longer truncated and the following works nicely:
xargs -0 printf '%s\n' < /proc/2755/cmdline