#!/usr/bin/env perl # # ptree for linux/darwin/solaris. I got sick of ps and pstree on linux. # ptree works on solaris fine, but doesn't exist for darwin/linux/etc... # # TODO: Allow passing a regexp instead of a pid and passing multiple pids. # local $ENV{"PATH"} = '/bin:/usr/bin:/sbin:/usr/sbin'; local $debug = 0; (exists($ENV{"DEBUG"})) ? $debug = 1 : undef; ($debug) ? local $| = 1 : undef; # disable buffering for STDERR and STDOUT sub debug_print { ($debug) && warn pop; }; local @cmd = ('/bin/ps'); local $init = 0; local $start = 1; if ($^O eq "linux"){ push(@cmd, 'ajxSw'); $start = 0; }elsif ($^O =~ m/darwin|freebsd/){ push(@cmd, 'Ajwww'); }elsif ($^O eq "solaris"){ @cmd = ('/usr/ucb/ps'); if ((-x "/bin/zonename") && !(qx(zonename) eq "global\n")){ $init = qx(pgrep zsched); chomp($init); $start = $init; }; push(@cmd, 'laxwww'); }else{ die "$^O isn't a known os to this script\n"; }; local @ps = qx(@cmd); ($?) ? die $cmd[0]." posix return code $?\n" : undef; local %proc; local @trail; (defined($ARGV[0])) ? $start = $ARGV[0] : undef; debug_print("Dump of raw line array data:\n"); foreach my $x (@ps){ my $cmd_index = 9; my $ppid = -1; my $pid = -1; next if ($x =~ m/.*PID.*/); my @line = split(m/\s+/,$x); splice(@line, 0, 1) unless ($line[0] =~ m/\d+/); splice(@line, 0, 1) if ("$line[0]" eq ""); if ($^O eq "linux"){ ($ppid,$pid) = @line; $cmd_index = 9; }elsif ($^O =~ m/darwin|freebsd/){ ($pid,$ppid) = @line; $cmd_index = 8; }elsif ($^O eq "solaris"){ (undef, undef, $pid,$ppid) = @line; for($x=$cmd_index; $x < 14; $x++){ $cmd_index = ($line[$x] =~ m/\d+\:\d+/) ? ($x + 1) : $cmd_index; }; }; my $command = join(' ', @line[$cmd_index..$#line++]); (!defined $proc{$pid}) ? $proc{$pid}->{"children"} = [ ] : undef; (!defined $proc{$ppid}) ? $proc{$ppid}->{"children"} = [ ] : undef; $proc{$pid}->{"pid"} = $pid; $proc{$pid}->{"command"} = $command; $proc{$pid}->{"ppid"} = $ppid; push(@{$proc{$ppid}->{"children"}},$pid); debug_print(join(',', @line[0..($#line-1)])."\n"); }; if ($debug){ debug_print("Done reading ps output\nDump of Hash\n"); foreach my $key (sort({$a <=> $b} (keys %proc))){ $ppid = $proc{$key}->{"ppid"}; $command = $proc{$key}->{"command"}; $children = join(',', @{$proc{$key}->{"children"}}); debug_print("PID<$key> PPID<$ppid> Children<$children> Command<$command>\n"); }; }; sub intrail{ my $crumb = pop; my $ret = 0; map {$ret++ if ($crumb == $_)} @trail; return $ret; }; sub addchildren{ my $start_node = pop; foreach my $child (@{$proc{$start_node}->{"children"}}){ next if (!defined($child) || ($start_node == $child)); debug_print("Start ($start_node) Add child $child.\n"); push(@trail, $child); addchildren($child); }; }; sub addparents{ my $start_node = pop; my $parent = $proc{$start_node}->{"ppid"}; return unless (defined($start_node)); return if (($parent == $start_node) || !(defined($parent))); # At the top of the tree, or in a zone debug_print("Start ($start_node) Add parent $parent.\n"); push(@trail, $parent); addparents($parent) if ($parent != $init); }; sub printnode{ my $start_node = pop; my $indent = pop; if (intrail($start_node)){ my $pid = $proc{$start_node}->{"pid"}; my $ppid = $proc{$start_node}->{"ppid"}; my $cmdline = $proc{$start_node}->{"command"}; my $indentation = ' 'x($indent); if ($pid == $init){ my $s = $^O." kernel"; if ($cmdline ne ''){ $s = $s." <$cmdline>"; }; $cmdline = $s; }; printf("%s%d %s\n", $indentation, $pid, $cmdline); foreach my $child (@{$proc{$start_node}->{"children"}}){ next if ($pid == $child); printnode($indent+1,$child); }; }; }; my $valid = 0; foreach my $j (keys %proc){ ($j == $start) ? $valid++ : undef; }; debug_print("Starting from <$start>, validity is<$valid>\n"); if ($valid){ if (!$start){ @trail = keys %proc; push(@trail, $start); push(@trail, 1); debug_print("Done with adding $start to trail.\n"); }else{ push(@trail, $start); debug_print("Adding children from $start.\n"); addchildren($start); debug_print("Adding parents from $start.\n"); addparents($start); }; @trail = sort(@trail); debug_print("Trail is: ".join(',', @trail)."\nStarting at pid $start\ninit pid $init\n"); print "PID COMMAND\n"; my $startindent = 0; (intrail($startindent)) ? $startindent = -1 : undef; printnode($startindent,$init); }else{ die "pid $start doesn't exist.\n"; }; exit 0;