~xnox/ubuntu/saucy/lxc/dep8

« back to all changes in this revision

Viewing changes to src/lxc/lxc-ps.in

  • Committer: Stéphane Graber
  • Date: 2013-02-18 15:20:18 UTC
  • mto: This revision was merged to the branch mainline in revision 190.
  • Revision ID: stgraber@ubuntu.com-20130218152018-ls2gi9hkqs2kuhj8
Tags: upstream-0.9.0~alpha3
Import upstream version 0.9.0~alpha3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/perl
2
 
#
3
 
# lxc-ps
4
 
#
5
 
# Authors:
6
 
# Daniel Lezcano <daniel.lezcano@free.fr>
 
1
#!/bin/sh
 
2
 
 
3
#
 
4
# lxc: linux Container library
7
5
 
8
6
# This library is free software; you can redistribute it and/or
9
7
# modify it under the terms of the GNU Lesser General Public
19
17
# License along with this library; if not, write to the Free Software
20
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
19
 
22
 
#
23
 
# This script allows to
24
 
# display processes information with related container name if available.
25
 
#
26
 
use strict;
27
 
 
28
 
 
29
 
# Some globals
30
 
 
31
 
our $PS_HEADERS;      # String containing headers of the ps output
32
 
our $PS_PID_INDEX;    # Index of the PID column in the ps headers
33
 
our @PS_LINES;        # Output lines of the ps command
34
 
 
35
 
our $LXC_DISPLAY = 0; # By default do not display container information
36
 
our %LXC_NAMES;       # Specified container names (if any)
37
 
 
38
 
sub get_container_names {
39
 
        my $ref_names = shift;
40
 
        my $lxcpath = '@LXCPATH@';
41
 
 
42
 
        open(active, "netstat -xa | grep $lxcpath |") or return;
43
 
        while(<active>) {
44
 
                chomp;
45
 
                s#.*$lxcpath/(.*)/command.*#$1#;
46
 
                push @$ref_names, $_;
47
 
        }
48
 
        close active;
49
 
}
50
 
 
51
 
sub get_cgroup {
52
 
        my $ref_cgroup = shift;
53
 
        my $mount_string;
54
 
 
55
 
        $mount_string=`mount -t cgroup |grep -E -e '^lxc '`;
56
 
        if ($mount_string) {
57
 
                # use the one 'lxc' cgroup mount if it exists
58
 
                chomp($mount_string);
59
 
                $$ref_cgroup=`echo "$mount_string" |cut -d' ' -f3`;
60
 
                chomp($$ref_cgroup);
61
 
        }
62
 
        # Otherwise (i.e. cgroup-bin) use the first cgroup mount
63
 
        $mount_string=`grep -m1 -E '^[^ \t]+[ \t]+[^ \t]+[ \t]+cgroup' /proc/self/mounts`;
64
 
        unless ($mount_string) {
65
 
                die "unable to find mounted cgroup" unless $$ref_cgroup;
66
 
        }
67
 
        chomp($mount_string);
68
 
        $$ref_cgroup=`echo "$mount_string" |cut -d' ' -f2`;
69
 
        chomp($$ref_cgroup);
70
 
        return;
71
 
}
72
 
 
73
 
sub get_pids_in_containers {
74
 
        my $ref_names = shift;
75
 
        my $ref_cgroup = shift;
76
 
        my $ref_pids = shift;
77
 
        my $init_cgroup = shift;
78
 
        my @pidlist;
79
 
 
80
 
        for (@{$ref_names}) {
81
 
                my $task_file = "$$ref_cgroup/$init_cgroup/lxc/$_/tasks";
82
 
 
83
 
                $LXC_NAMES{$_} = 1;
84
 
                open(tasks, "cat $task_file 2>/dev/null |") or next;
85
 
                while (<tasks>) {
86
 
                        chomp $_;
87
 
                        push @pidlist, $_;
88
 
                }
89
 
                close tasks;
90
 
        }
91
 
        $$ref_pids = join(',', @pidlist);
92
 
}
93
 
 
94
 
sub reclaim_pid_index {
95
 
    my @headers = split " ", $PS_HEADERS;
96
 
    for my $i (0 .. $#headers) {
97
 
        if ($headers[$i] eq "PID") {
98
 
            $PS_PID_INDEX = $i;
99
 
            return;
100
 
        }
101
 
    }
102
 
    print "Cannot find ps PID column !\n";
103
 
    exit 1;
104
 
}
105
 
 
106
 
sub execute_ps {
107
 
    open(ps, "ps @_ |") or die "Cannot execute ps command: $!\n";
108
 
 
109
 
    $PS_HEADERS = <ps>;
110
 
    reclaim_pid_index;
111
 
 
112
 
    while (<ps>) {
113
 
        push @PS_LINES, $_;
114
 
    }
115
 
    close ps;
116
 
}
117
 
 
118
 
sub get_init_cgroup {
119
 
    my $filename = "/proc/1/cgroup";
120
 
    open(LXC, "$filename");
121
 
    my @cgroup = <LXC>;
122
 
    close LXC;
123
 
    my $container = '';
124
 
    foreach ( @cgroup ) {
125
 
        chomp;
126
 
        # find the container name after :/
127
 
        s/.*:\///o;
128
 
    }
129
 
    return $container;
130
 
}
131
 
 
132
 
sub get_container {
133
 
    my $pid = shift;
134
 
    my $filename = "/proc/$pid/cgroup";
135
 
    open(LXC, "$filename");
136
 
    # read all lines at once
137
 
    my @cgroup = <LXC>;
138
 
    close LXC;
139
 
    my $container = '';
140
 
    foreach ( @cgroup ) {
141
 
        chomp;
142
 
        # find the container name after :/
143
 
        s/.*:\///o;
144
 
        # chop off everything up to 'lxc/'
145
 
        s/lxc\///o;
146
 
        $container = $_;
147
 
    }
148
 
    return $container;
149
 
}
150
 
 
151
 
sub display_headers {
152
 
    printf "%-10s %s", "CONTAINER", $PS_HEADERS;
153
 
}
154
 
 
155
 
sub display_usage {
156
 
    print <<EOF;
157
 
Usage: lxc-ps [--help] [--usage] [-n|--name NAME...] [--lxc] [-- ps options]
158
 
EOF
159
 
}
160
 
 
161
 
sub display_help {
162
 
    display_usage;
163
 
    print <<EOF;
164
 
 
165
 
Display processes information with related container name if available.
166
 
 
167
 
Options:
168
 
--help     Display this help.
169
 
--usage    Display the command usage.
170
 
--name     Display processes related to given containers.
171
 
           Containers are identified by a comma separated list of
172
 
           their names.
173
 
--lxc      Display processes related to all lxc containers.
174
 
 
175
 
Other available options correspond to the ps ones, see the ps manual
176
 
or try a 'ps --help' for further details.
177
 
EOF
178
 
}
179
 
 
180
 
use Getopt::Long qw(:config pass_through);
181
 
 
182
 
my $arg_help  = '';
183
 
my $arg_usage = '';
184
 
my $arg_lxc   = '';
185
 
my @arg_name;
186
 
my $init_cgroup = '/';
187
 
 
188
 
GetOptions('help'   => \$arg_help,
189
 
           'usage'  => \$arg_usage,
190
 
           'lxc'    => \$arg_lxc,
191
 
           'name=s' => \@arg_name);
192
 
 
193
 
@arg_name = split(/,/, join(',', @arg_name));
194
 
 
195
 
# Some help
196
 
if ($arg_help)  {display_help; exit 0;}
197
 
if ($arg_usage) {display_usage; exit 0;}
198
 
 
199
 
if ($ARGV[0] == '--') {
200
 
        shift @ARGV;
201
 
}
202
 
 
203
 
# Should we filter processes related to containers
204
 
if ($arg_lxc) {
205
 
        $LXC_DISPLAY = 1;
206
 
        get_container_names \@arg_name;
207
 
}
208
 
if (@arg_name > 0) {
209
 
        my $cgroup;
210
 
        my $pid_list;
211
 
        $LXC_DISPLAY = 2;
212
 
 
213
 
        $init_cgroup = get_init_cgroup();
214
 
        get_cgroup \$cgroup;
215
 
        get_pids_in_containers(\@arg_name, \$cgroup, \$pid_list, $init_cgroup);
216
 
        if ($pid_list) {
217
 
                @ARGV = ("-p $pid_list",@ARGV);
218
 
        }
219
 
}
220
 
 
221
 
execute_ps @ARGV;
222
 
 
223
 
display_headers;
224
 
for (@PS_LINES) {
225
 
    my @a = split;
226
 
    my $container = get_container $a[$PS_PID_INDEX];
227
 
    if ($LXC_DISPLAY == 2 and not $LXC_NAMES{$container}) {next;}
228
 
    if ($LXC_DISPLAY == 1 and $container eq '') {next;}
229
 
    printf "%-10s %s", $container, $_;
230
 
}
231
 
 
232
 
exit 0;
 
20
usage()
 
21
{
 
22
    echo "usage: $(basename $0) [--lxc | --name NAME] [--] [PS_OPTIONS...]" >&2
 
23
}
 
24
 
 
25
help() {
 
26
    usage
 
27
    echo >&2
 
28
    echo "List current processes with container names." >&2
 
29
    echo >&2
 
30
    echo "  --lxc         show processes in all containers" >&2
 
31
    echo "  --name NAME   show processes in the specified container" >&2
 
32
    echo "                 (multiple containers can be separated by commas)" >&2
 
33
    echo "  PS_OPTIONS    ps command options (see \`ps --help')" >&2
 
34
}
 
35
 
 
36
get_parent_cgroup()
 
37
{
 
38
    local hierarchies hierarchy fields subsystems init_cgroup mountpoint
 
39
 
 
40
    parent_cgroup=""
 
41
 
 
42
    # Obtain a list of hierarchies that contain one or more subsystems
 
43
    hierarchies=$(tail -n +2 /proc/cgroups | cut -f 2)
 
44
 
 
45
    # Iterate through the list until a suitable hierarchy is found
 
46
    for hierarchy in $hierarchies; do
 
47
        # Obtain information about the init process in the hierarchy
 
48
        fields=$(grep -E "^$hierarchy:" /proc/1/cgroup | head -n 1)
 
49
        if [ -z "$fields" ]; then continue; fi
 
50
        fields=${fields#*:}
 
51
 
 
52
        # Get a comma-separated list of the hierarchy's subsystems
 
53
        subsystems=${fields%:*}
 
54
 
 
55
        # Get the cgroup of the init process in the hierarchy
 
56
        init_cgroup=${fields#*:}
 
57
 
 
58
        # Get the filesystem mountpoint of the hierarchy
 
59
        mountpoint=$(awk -v subsysregex="(^|,)$subsystems(,|\$)" \
 
60
            '$3 == "cgroup" && $4 ~ subsysregex {print $2}' /proc/self/mounts)
 
61
        if [ -z "$mountpoint" ]; then continue; fi
 
62
 
 
63
        # Return the absolute path to the containers' parent cgroup
 
64
        # (do not append '/lxc' if the hierarchy contains the 'ns' subsystem)
 
65
        case ",$subsystems," in
 
66
            *,ns,*) parent_cgroup="${mountpoint}${init_cgroup%/}";;
 
67
            *) parent_cgroup="${mountpoint}${init_cgroup%/}/lxc";;
 
68
        esac
 
69
        break
 
70
    done
 
71
}
 
72
 
 
73
containers=""
 
74
list_container_processes=0
 
75
while true; do
 
76
    case $1 in
 
77
        -h|--help)
 
78
            help; exit 1;;
 
79
        -n|--name)
 
80
            containers=$2; list_container_processes=1; shift 2;;
 
81
        --lxc)
 
82
            list_container_processes=1; shift;;
 
83
        --)
 
84
            shift; break;;
 
85
        *)
 
86
            break;;
 
87
        esac
 
88
done
 
89
 
 
90
if [ "$list_container_processes" -eq "1" ]; then
 
91
    set -- -e $@
 
92
fi
 
93
 
 
94
get_parent_cgroup
 
95
if [ ! -d "$parent_cgroup" ]; then
 
96
    echo "$(basename $0): no cgroup mount point found" >&2
 
97
    exit 1
 
98
fi
 
99
 
 
100
if [ -z "$containers" ]; then
 
101
    containers="$(find $parent_cgroup -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sed 's:.*/::')"
 
102
fi
 
103
 
 
104
container_field_width=9
 
105
tasks_files=
 
106
for container in ${containers}; do
 
107
    if [ "${#container}" -gt "$container_field_width" ]; then
 
108
        container_field_width=${#container}
 
109
    fi
 
110
 
 
111
    if [ -f "$parent_cgroup/$container/tasks" ]; then
 
112
        tasks_files="$tasks_files $parent_cgroup/$container/tasks"
 
113
    fi
 
114
done
 
115
 
 
116
# first file is stdin, the rest are the container tasks
 
117
ps "$@" | awk -v container_field_width="$container_field_width" \
 
118
    -v list_container_processes="$list_container_processes" '
 
119
# first line is PS header
 
120
NR == 1 {
 
121
    # find pid field index
 
122
    for (i = 1; i<=NF; i++)
 
123
        if ($i == "PID") {
 
124
            pididx = i
 
125
            break
 
126
        }
 
127
    if (pididx == "") {
 
128
        print("No PID field found") > "/dev/stderr"
 
129
        exit 1
 
130
    }
 
131
    header = $0
 
132
    next
 
133
}
 
134
 
 
135
# store lines from ps with pid as index
 
136
NR == FNR {
 
137
    ps_line[NR] = $0
 
138
    pid_of_line[NR] = $pididx
 
139
    next
 
140
}
 
141
 
 
142
# find container name from filename on first line
 
143
FNR == 1 {
 
144
    container = FILENAME
 
145
    sub(/\/tasks/, "", container)
 
146
    sub(/.*\//, "", container)
 
147
}
 
148
 
 
149
# container tasks
 
150
{
 
151
    container_of_pid[$0] = container
 
152
}
 
153
 
 
154
END {
 
155
    printf("%-" container_field_width "s %s\n", "CONTAINER", header)
 
156
    for (i in ps_line) {
 
157
        container = container_of_pid[pid_of_line[i]]
 
158
        if (list_container_processes == 0 || container != "")
 
159
            printf("%-" container_field_width "s %s\n", container, ps_line[i])
 
160
    }
 
161
}
 
162
 
 
163
' - $tasks_files