Commit 8edcbf33 by David Ward Committed by Daniel Lezcano

rewrite lxc-ps

Use bash instead of perl; eliminates final lxc dependency on perl (beneficial for minimal operating system environments). Modify the cgroup search to only use hierarchies that contain one or more subsystems. When searching, if a hierarchy contains the 'ns' subsystem, do not append '/lxc' to the parent cgroup. Maintain column spacing. Expand container name column as necessary. Properly handle spaces in 'ps' output that are not field separators (for example, try 'lxc-ps -o pid,args'). Fix file mode in repository. Signed-off-by: 's avatarDavid Ward <david.ward@ll.mit.edu> Signed-off-by: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com>
parent 5e52afd5
#!/usr/bin/perl #!/bin/bash
#
# lxc-ps
# #
# Authors: # lxc: linux Container library
# Daniel Lezcano <daniel.lezcano@free.fr>
# This library is free software; you can redistribute it and/or # This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public # modify it under the terms of the GNU Lesser General Public
...@@ -19,214 +17,127 @@ ...@@ -19,214 +17,127 @@
# License along with this library; if not, write to the Free Software # License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# usage()
# This script allows to {
# display processes information with related container name if available. echo "usage: $(basename $0) [--lxc | --name NAME] [--] [PS_OPTIONS...]" >&2
#
use strict;
# Some globals
our $PS_HEADERS; # String containing headers of the ps output
our $PS_PID_INDEX; # Index of the PID column in the ps headers
our @PS_LINES; # Output lines of the ps command
our $LXC_DISPLAY = 0; # By default do not display container information
our %LXC_NAMES; # Specified container names (if any)
sub get_container_names {
my $ref_names = shift;
my $lxcpath = '@LXCPATH@';
open(active, "netstat -xa | grep $lxcpath |") or return;
while(<active>) {
chomp;
s#.*$lxcpath/(.*)/command.*#$1#;
push @$ref_names, $_;
}
close active;
} }
sub get_cgroup { help() {
my $ref_cgroup = shift; usage
my $mount_string; echo >&2
echo "List current processes with container names." >&2
$mount_string=`mount -t cgroup |grep -E -e '^lxc '`; echo >&2
if ($mount_string) { echo " --lxc show processes in all containers" >&2
# use the one 'lxc' cgroup mount if it exists echo " --name NAME show processes in the specified container" >&2
chomp($mount_string); echo " (multiple containers can be separated by commas)" >&2
$$ref_cgroup=`echo "$mount_string" |cut -d' ' -f3`; echo " PS_OPTIONS ps command options (see \`ps --help')" >&2
chomp($$ref_cgroup);
}
# Otherwise (i.e. cgroup-bin) use the first cgroup mount
$mount_string=`grep -m1 -E '^[^ \t]+[ \t]+[^ \t]+[ \t]+cgroup' /proc/self/mounts`;
unless ($mount_string) {
die "unable to find mounted cgroup" unless $$ref_cgroup;
}
chomp($mount_string);
$$ref_cgroup=`echo "$mount_string" |cut -d' ' -f2`;
chomp($$ref_cgroup);
return;
} }
sub get_pids_in_containers { get_parent_cgroup()
my $ref_names = shift; {
my $ref_cgroup = shift; local hierarchies hierarchy fields subsystems init_cgroup mountpoint
my $ref_pids = shift;
my $init_cgroup = shift; parent_cgroup=""
my @pidlist;
# Obtain a list of hierarchies that contain one or more subsystems
for (@{$ref_names}) { hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2)
my $task_file = "$$ref_cgroup/$init_cgroup/lxc/$_/tasks";
# Iterate through the list until a suitable hierarchy is found
$LXC_NAMES{$_} = 1; for hierarchy in $hierarchies; do
open(tasks, "cat $task_file 2>/dev/null |") or next; # Obtain information about the init process in the hierarchy
while (<tasks>) { fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1)
chomp $_; if [ -z "$fields" ]; then continue; fi
push @pidlist, $_; fields=${fields#*:}
}
close tasks; # Get a comma-separated list of the hierarchy's subsystems
} subsystems=${fields%:*}
$$ref_pids = join(',', @pidlist);
# Get the cgroup of the init process in the hierarchy
init_cgroup=${fields#*:}
# Get the filesystem mountpoint of the hierarchy
mountpoint=$(grep -E "^cgroup [^ ]+ [^ ]+ ([^ ]+,)?$subsystems(,[^ ]+)? " /proc/self/mounts | cut -d ' ' -f 2)
if [ -z "$mountpoint" ]; then continue; fi
# Return the absolute path to the containers' parent cgroup
# (do not append '/lxc' if the hierarchy contains the 'ns' subsystem)
if [[ ",$subsystems," == *,ns,* ]]; then
parent_cgroup="${mountpoint}${init_cgroup%/}"
else
parent_cgroup="${mountpoint}${init_cgroup%/}/lxc"
fi
break
done
} }
sub reclaim_pid_index { containers=""
my @headers = split " ", $PS_HEADERS; list_container_processes=0
for my $i (0 .. $#headers) { for i in "$@"; do
if ($headers[$i] eq "PID") { case $i in
$PS_PID_INDEX = $i; --help)
return; help; exit 1;;
} --name)
} containers=$2; list_container_processes=1; shift 2;;
print "Cannot find ps PID column !\n"; --lxc)
exit 1; list_container_processes=1; shift;;
} --)
shift; break;;
sub execute_ps { *)
open(ps, "ps @_ |") or die "Cannot execute ps command: $!\n"; break;;
esac
$PS_HEADERS = <ps>; done
reclaim_pid_index;
if [ "$list_container_processes" -eq "1" ]; then
while (<ps>) { set -- -e $@
push @PS_LINES, $_; fi
}
close ps; get_parent_cgroup
} if [ ! -d "$parent_cgroup" ]; then
echo "$(basename $0): no cgroup mount point found" >&2
sub get_init_cgroup { exit 1
my $filename = "/proc/1/cgroup"; fi
open(LXC, "$filename");
my @cgroup = <LXC>; declare -a container_of_pid
close LXC; container_field_width=9
my $container = ''; IFS=","
foreach ( @cgroup ) { if [ -z "$containers" ]; then
chomp; containers=( $(find $parent_cgroup -mindepth 1 -maxdepth 1 -type d -printf "%f," 2>/dev/null) )
# find the container name after :/ else
s/.*:\///o; containers=( $containers )
} fi
return $container;
} declare -i pid
IFS=$'\n'
sub get_container { for container in ${containers[@]}; do
my $pid = shift; if [ "${#container}" -gt "$container_field_width" ]; then
my $filename = "/proc/$pid/cgroup"; container_field_width=${#container}
open(LXC, "$filename"); fi
# read all lines at once
my @cgroup = <LXC>; if [ -f "$parent_cgroup/$container/tasks" ]; then
close LXC; while read pid; do
my $container = ''; container_of_pid[$pid]=$container
foreach ( @cgroup ) { done < "$parent_cgroup/$container/tasks"
chomp; fi
# find the container name after :/ done
s/.*:\///o;
# chop off everything up to 'lxc/' declare -i line_pid_end_position
s/lxc\///o; while read line; do
$container = $_; if [ -z "$line_pid_end_position" ]; then
} if [[ "$line" != *" PID"* ]]; then
return $container; echo "$(basename $0): no PID column found in \`ps' output" >&2
} exit 1
fi
sub display_headers {
printf "%-10s %s", "CONTAINER", $PS_HEADERS; buffer=${line%" PID"*}
} let line_pid_end_position=${#buffer}+4
printf "%-${container_field_width}s %s\n" "CONTAINER" "$line"
sub display_usage { continue
print <<EOF; fi
Usage: lxc-ps [--help] [--usage] [-n|--name NAME...] [--lxc] [-- ps options]
EOF buffer=${line:0:$line_pid_end_position}
} pid=${buffer##* }
if [ "$list_container_processes" -eq "0" -o ! -z "${container_of_pid[pid]}" ]; then
sub display_help { printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line"
display_usage; fi
print <<EOF; done < <(ps "$@")
Display processes information with related container name if available.
Options:
--help Display this help.
--usage Display the command usage.
--name Display processes related to given containers.
Containers are identified by a comma separated list of
their names.
--lxc Display processes related to all lxc containers.
Other available options correspond to the ps ones, see the ps manual
or try a 'ps --help' for further details.
EOF
}
use Getopt::Long qw(:config pass_through);
my $arg_help = '';
my $arg_usage = '';
my $arg_lxc = '';
my @arg_name;
my $init_cgroup = '/';
GetOptions('help' => \$arg_help,
'usage' => \$arg_usage,
'lxc' => \$arg_lxc,
'name=s' => \@arg_name);
@arg_name = split(/,/, join(',', @arg_name));
# Some help
if ($arg_help) {display_help; exit 0;}
if ($arg_usage) {display_usage; exit 0;}
if ($ARGV[0] == '--') {
shift @ARGV;
}
# Should we filter processes related to containers
if ($arg_lxc) {
$LXC_DISPLAY = 1;
get_container_names \@arg_name;
}
if (@arg_name > 0) {
my $cgroup;
my $pid_list;
$LXC_DISPLAY = 2;
$init_cgroup = get_init_cgroup();
get_cgroup \$cgroup;
get_pids_in_containers(\@arg_name, \$cgroup, \$pid_list, $init_cgroup);
if ($pid_list) {
@ARGV = ("-p $pid_list",@ARGV);
}
}
execute_ps @ARGV;
display_headers;
for (@PS_LINES) {
my @a = split;
my $container = get_container $a[$PS_PID_INDEX];
if ($LXC_DISPLAY == 2 and not $LXC_NAMES{$container}) {next;}
if ($LXC_DISPLAY == 1 and $container eq '') {next;}
printf "%-10s %s", $container, $_;
}
exit 0;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment