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: David Ward <david.w...@ll.mit.edu> --- src/lxc/lxc-ps.in | 329 +++++++++++++++++++--------------------------------- 1 files changed, 120 insertions(+), 209 deletions(-) mode change 100755 => 100644 src/lxc/lxc-ps.in diff --git a/src/lxc/lxc-ps.in b/src/lxc/lxc-ps.in old mode 100755 new mode 100644 index 2fa7b8b..a9923f0 --- a/src/lxc/lxc-ps.in +++ b/src/lxc/lxc-ps.in @@ -1,9 +1,7 @@ -#!/usr/bin/perl -# -# lxc-ps +#!/bin/bash + # -# Authors: -# Daniel Lezcano <daniel.lezc...@free.fr> +# lxc: linux Container library # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,214 +17,127 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# This script allows to -# display processes information with related container name if available. -# -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; +usage() +{ + echo "usage: $(basename $0) [--lxc | --name NAME] [--] [PS_OPTIONS...]" >&2 } -sub get_cgroup { - my $ref_cgroup = shift; - my $mount_string; - - $mount_string=`mount -t cgroup |grep -E -e '^lxc '`; - if ($mount_string) { - # use the one 'lxc' cgroup mount if it exists - chomp($mount_string); - $$ref_cgroup=`echo "$mount_string" |cut -d' ' -f3`; - 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; +help() { + usage + echo >&2 + echo "List current processes with container names." >&2 + echo >&2 + echo " --lxc show processes in all containers" >&2 + echo " --name NAME show processes in the specified container" >&2 + echo " (multiple containers can be separated by commas)" >&2 + echo " PS_OPTIONS ps command options (see \`ps --help')" >&2 } -sub get_pids_in_containers { - my $ref_names = shift; - my $ref_cgroup = shift; - my $ref_pids = shift; - my $init_cgroup = shift; - my @pidlist; - - for (@{$ref_names}) { - my $task_file = "$$ref_cgroup/$init_cgroup/lxc/$_/tasks"; - - $LXC_NAMES{$_} = 1; - open(tasks, "cat $task_file 2>/dev/null |") or next; - while (<tasks>) { - chomp $_; - push @pidlist, $_; - } - close tasks; - } - $$ref_pids = join(',', @pidlist); +get_parent_cgroup() +{ + local hierarchies hierarchy fields subsystems init_cgroup mountpoint + + parent_cgroup="" + + # Obtain a list of hierarchies that contain one or more subsystems + hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2) + + # Iterate through the list until a suitable hierarchy is found + for hierarchy in $hierarchies; do + # Obtain information about the init process in the hierarchy + fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1) + if [ -z "$fields" ]; then continue; fi + fields=${fields#*:} + + # Get a comma-separated list of the hierarchy's subsystems + subsystems=${fields%:*} + + # 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 { - my @headers = split " ", $PS_HEADERS; - for my $i (0 .. $#headers) { - if ($headers[$i] eq "PID") { - $PS_PID_INDEX = $i; - return; - } - } - print "Cannot find ps PID column !\n"; - exit 1; -} - -sub execute_ps { - open(ps, "ps @_ |") or die "Cannot execute ps command: $!\n"; - - $PS_HEADERS = <ps>; - reclaim_pid_index; - - while (<ps>) { - push @PS_LINES, $_; - } - close ps; -} - -sub get_init_cgroup { - my $filename = "/proc/1/cgroup"; - open(LXC, "$filename"); - my @cgroup = <LXC>; - close LXC; - my $container = ''; - foreach ( @cgroup ) { - chomp; - # find the container name after :/ - s/.*:\///o; - } - return $container; -} - -sub get_container { - my $pid = shift; - my $filename = "/proc/$pid/cgroup"; - open(LXC, "$filename"); - # read all lines at once - my @cgroup = <LXC>; - close LXC; - my $container = ''; - foreach ( @cgroup ) { - chomp; - # find the container name after :/ - s/.*:\///o; - # chop off everything up to 'lxc/' - s/lxc\///o; - $container = $_; - } - return $container; -} - -sub display_headers { - printf "%-10s %s", "CONTAINER", $PS_HEADERS; -} - -sub display_usage { - print <<EOF; -Usage: lxc-ps [--help] [--usage] [-n|--name NAME...] [--lxc] [-- ps options] -EOF -} - -sub display_help { - display_usage; - print <<EOF; - -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, $_; -} +containers="" +list_container_processes=0 +for i in "$@"; do + case $i in + --help) + help; exit 1;; + --name) + containers=$2; list_container_processes=1; shift 2;; + --lxc) + list_container_processes=1; shift;; + --) + shift; break;; + *) + break;; + esac +done + +if [ "$list_container_processes" -eq "1" ]; then + set -- -e $@ +fi + +get_parent_cgroup +if [ ! -d "$parent_cgroup" ]; then + echo "$(basename $0): no cgroup mount point found" >&2 + exit 1 +fi + +declare -a container_of_pid +container_field_width=9 +IFS="," +if [ -z "$containers" ]; then + containers=( $(find $parent_cgroup -mindepth 1 -maxdepth 1 -type d -printf "%f," 2>/dev/null) ) +else + containers=( $containers ) +fi + +declare -i pid +IFS=$'\n' +for container in ${containers[@]}; do + if [ "${#container}" -gt "$container_field_width" ]; then + container_field_width=${#container} + fi + + if [ -f "$parent_cgroup/$container/tasks" ]; then + while read pid; do + container_of_pid[$pid]=$container + done < "$parent_cgroup/$container/tasks" + fi +done + +declare -i line_pid_end_position +while read line; do + if [ -z "$line_pid_end_position" ]; then + if [[ "$line" != *" PID"* ]]; then + echo "$(basename $0): no PID column found in \`ps' output" >&2 + exit 1 + fi + + buffer=${line%" PID"*} + let line_pid_end_position=${#buffer}+4 + printf "%-${container_field_width}s %s\n" "CONTAINER" "$line" + continue + fi + + buffer=${line:0:$line_pid_end_position} + pid=${buffer##* } + if [ "$list_container_processes" -eq "0" -o ! -z "${container_of_pid[pid]}" ]; then + printf "%-${container_field_width}s %s\n" "${container_of_pid[pid]}" "$line" + fi +done < <(ps "$@") -exit 0; -- 1.7.4.1 ------------------------------------------------------------------------------ This SF email is sponsosred by: Try Windows Azure free for 90 days Click Here http://p.sf.net/sfu/sfd2d-msazure _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel