pstree.pl

Вопросы и обсуждения, касающиеся программирования на Perl.
ypaseka
Сообщения: 10
Зарегистрирован: 20 янв 2015, 19:13
Темы: 2
Статус: Не в сети

pstree.pl

Сообщение ypaseka » 21 янв 2015, 03:22

Еще вопрос: скрипт работающий как pstree, должна существовать возможность показания всех процессов или связанных с пользователем( до него принадлежащих, вплоть до init). В случае презентации дерева процессов связанных с пользователем, все процессы не связанные с ним обозначить как параметр. Записать его как txt или png(modul GD)

Можно из это сделать что то подобное?

Код: Выделить всё

#!perl
use strict;
use warnings;
use List::MoreUtils qw/any/;

main();

sub main {
    opendir my $dh, "/proc" or die $!;
    my @pid_dirs = grep { m{^[1-9][0-9]*$} } readdir $dh;
    closedir $dh;

    my @program_infos;
    for my $pid (@pid_dirs) {
        my $program_info = read_program_status($pid);
        next unless defined $program_info;

        push @program_infos, $program_info;
    }

    my %pid_name;
    map { $pid_name{$_->{pid}} = $_->{name} } @program_infos;

    my %name_pid;
    map { $name_pid{$_->{name}} = $_->{pid} } @program_infos;

    my $ps_tree = {};
    my @parents;
    my @children;
    for my $program_info (@program_infos) {
        my $ppid = $program_info->{ppid};
	next if $ppid == 0;

        my $pid = $program_info->{pid};
        $ps_tree->{$ppid}->{$pid} = 1;
    }

    my @ppids = keys %{$ps_tree};
    construct_ps_tree($ps_tree, $ps_tree, \@ppids);

    my $name_tree = {};
    ps_tree_to_name_tree($ps_tree, $name_tree, \%pid_name);
    print_tree($name_tree, \%name_pid, 0);
    
}

sub ps_tree_to_name_tree {
    my ($ps_tree, $name_tree, $pid_name) = @_;

    for my $pid (keys $ps_tree) {
        my $name = $pid_name->{$pid} || '0';
        if (ref $ps_tree->{$pid} eq "HASH") {
            $name_tree->{$name} = {};
            ps_tree_to_name_tree($ps_tree->{$pid}, $name_tree->{$name}, $pid_name);
        } else {
            $name_tree->{$name} = 1;
        }
    }
}

sub construct_ps_tree {
    my ($ps_tree, $parent, $ppids) = @_;

    my @deleted_pids;
    for my $pid (keys %{$parent}) {
        next unless ref $parent->{$pid} eq 'HASH';
        for my $child_pid (keys %{$parent->{$pid}}) {
            if (any { $_ == $child_pid } @{$ppids}) {
                $parent->{$pid}->{$child_pid} = $ps_tree->{$child_pid};
                push @deleted_pids, $child_pid;
                my @children = keys %{$ps_tree->{$child_pid}};
                construct_ps_tree($ps_tree, $parent->{$pid}->{$child_pid}, \@children);
            }
        }
    }

    for my $deleted_pid (@deleted_pids) {
        delete $ps_tree->{$deleted_pid};
    }
}

sub print_tree {
    my ($name_tree, $name_pid, $indent_level) = @_;

    my $indent = "    " x $indent_level;
    for my $name (sort keys %{$name_tree}) {
        printf "%s%s(%s)\n", $indent, $name, $name_pid->{$name} || $name;
        if (ref $name_tree->{$name} eq 'HASH') {
            print_tree($name_tree->{$name}, $name_pid, $indent_level+1);
        }
    }
}

sub read_program_status {
    my $pid = shift;

    my %program_info;
    open my $fh, '<', "/proc/$pid/status" or return;
    while (defined(my $line = <$fh>)) {
        chomp $line;

        if ($line =~ m{^([^:]+):\s*(.+)$}) {
            $program_info{lc $1} = $2;
        }
    }
    close $fh;

    return \%program_info;
}

Аватара пользователя
ZEN
Администратор
Сообщения: 1350
Зарегистрирован: 27 сен 2012, 18:23
Темы: 206
Откуда: Украина, Одесса
Статус: Не в сети

Re: pstree.pl

Сообщение ZEN » 21 янв 2015, 12:55

Тема перемещена в раздел Perl.

По причине работы - в данный момент я не могу посмотреть скрипт, так что будем надеяться что вечером посмотрю и отпишусь что можно сделать.

UPD Бегло успел посмотреть, скрипт вывод строит. Так что не хватает дописать поддержки GD и grep по пользователю..
бог создал труд и обезьяну
чтоб получился человек
а вот пингвина он не трогал
тот сразу вышел хорошо

ypaseka
Сообщения: 10
Зарегистрирован: 20 янв 2015, 19:13
Темы: 2
Статус: Не в сети

Re: pstree.pl

Сообщение ypaseka » 22 янв 2015, 18:41

Как там можно дописать?

Аватара пользователя
ZEN
Администратор
Сообщения: 1350
Зарегистрирован: 27 сен 2012, 18:23
Темы: 206
Откуда: Украина, Одесса
Статус: Не в сети

Re: pstree.pl

Сообщение ZEN » 22 янв 2015, 20:12

Ох... не смотрел еще :)
По идее все не так уже сложно. Установить пакет libgd-text-perl и используя модуль GD отрисовать текст в картинку.. Но нужно сесть и заняться
бог создал труд и обезьяну
чтоб получился человек
а вот пингвина он не трогал
тот сразу вышел хорошо

ypaseka
Сообщения: 10
Зарегистрирован: 20 янв 2015, 19:13
Темы: 2
Статус: Не в сети

Re: pstree.pl

Сообщение ypaseka » 22 янв 2015, 20:58

Помогите пожалуйста, честно, не умею в perl и уже нет времени учится:(

Аватара пользователя
ZEN
Администратор
Сообщения: 1350
Зарегистрирован: 27 сен 2012, 18:23
Темы: 206
Откуда: Украина, Одесса
Статус: Не в сети

Re: pstree.pl

Сообщение ZEN » 23 янв 2015, 00:55

Так... Пока что сделал вариант вывода процессов по указанному юзеру. Либо сразу по всем, если не указан ключ. Завтра постараюсь сделать вывод на картинку. Вот текущий код:

Код: Выделить всё

#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw/any/;
use Getopt::Long;
use Data::Dumper;

my $user = '';
my $output_to_image = undef;

main();

sub usage {
    print STDOUT "$0 --user=$ENV{USER}\n";
    print STDOUT "or\n";
    print STDOUT "$0 --user=$ENV{USER} --to-image\n";
}

sub main {

    GetOptions(
        "user=s" => \$user,
        "to-image" => \$output_to_image
    ) or usage() and exit 1;

    my (undef, undef, $uid) = getpwnam($user) if $user;
    if ($user and not defined $uid) {
        print "Unknown user - $user\n";
        exit 2;
    }

    opendir my $dh, "/proc" or die $!;
    my @pid_dirs = grep { m{^[1-9][0-9]*$} } readdir $dh;
    closedir $dh;

    my @program_infos;
    for my $pid (@pid_dirs) {
        my $program_info = read_program_status($pid);

        next unless defined $program_info;
        ($program_info->{uid}) = split( ' ', $program_info->{uid});
        next if ($user && $uid != $program_info->{uid});

        push @program_infos, $program_info;
    }

    if ($user && $uid) {
        # Fix non-existing ppid
        for my $pid (@program_infos) {
            if (!any { $_->{pid} == $pid->{ppid} } @program_infos) {
                $pid->{ppid} = 0;
            }
        }
    }

    my %pid_name;
    map { $pid_name{$_->{pid}} = $_->{name} } @program_infos;

    my %name_pid;
    map { $name_pid{$_->{name}} = $_->{pid} } @program_infos;

    my $ps_tree = {};
    my @parents;
    my @children;
    for my $program_info (@program_infos) {
        my $ppid = $program_info->{ppid};
    next if $ppid == 0 && !$user;

        my $pid = $program_info->{pid};
        $ps_tree->{$ppid}->{$pid} = 1;
    }

    my @ppids = keys %{$ps_tree};
    construct_ps_tree($ps_tree, $ps_tree, \@ppids);

    my $name_tree = {};
    ps_tree_to_name_tree($ps_tree, $name_tree, \%pid_name);
    print_tree($name_tree, \%name_pid, 0);
}

sub ps_tree_to_name_tree {
    my ($ps_tree, $name_tree, $pid_name) = @_;

    for my $pid (keys $ps_tree) {
        my $name = $pid_name->{$pid} || '0';
        if (ref $ps_tree->{$pid} eq "HASH") {
            $name_tree->{$name} = {};
            ps_tree_to_name_tree($ps_tree->{$pid}, $name_tree->{$name}, $pid_name);
        } else {
            $name_tree->{$name} = 1;
        }
    }
}

sub construct_ps_tree {
    my ($ps_tree, $parent, $ppids) = @_;

    my @deleted_pids;
    for my $pid (keys %{$parent}) {
        next unless ref $parent->{$pid} eq 'HASH';
        for my $child_pid (keys %{$parent->{$pid}}) {
            if (any { $_ == $child_pid } @{$ppids}) {
                $parent->{$pid}->{$child_pid} = $ps_tree->{$child_pid};
                push @deleted_pids, $child_pid;
                my @children = keys %{$ps_tree->{$child_pid}};
                construct_ps_tree($ps_tree, $parent->{$pid}->{$child_pid}, \@children);
            }
        }
    }

    for my $deleted_pid (@deleted_pids) {
        delete $ps_tree->{$deleted_pid};
    }
}

sub print_tree {
    my ($name_tree, $name_pid, $indent_level) = @_;

    my $indent = "    " x $indent_level;
    for my $name (sort keys %{$name_tree}) {
        my $pname = $name_pid->{$name} || $name;
        if ($pname == "0") {
            if (ref $name_tree->{$name} eq 'HASH') {
                print_tree($name_tree->{$name}, $name_pid, $indent_level);
            }
        } else {
            printf "%s%s(%s)\n", $indent, $name, $name_pid->{$name} || $name;
            if (ref $name_tree->{$name} eq 'HASH') {
                print_tree($name_tree->{$name}, $name_pid, $indent_level+1);
            }
        }
    }
}

sub read_program_status {
    my $pid = shift;

    my %program_info;
    open my $fh, '<', "/proc/$pid/status" or return;
    while (defined(my $line = <$fh>)) {
        chomp $line;

        if ($line =~ m{^([^:]+):\s*(.+)$}) {
            $program_info{lc $1} = $2;
        }
    }
    close $fh;

    return \%program_info;
}
Пример:

Код: Выделить всё

zen@devel:/tmp$ ./pstree.pl --user=zen
clipit(2767)
compton(2774)
dbus-daemon(2742)
dbus-launch(2741)
gconfd-2(2804)
gnome-keyring-d(2569)
gvfsd(2747)
gxkb(2769)
nm-applet(2783)
openbox(2645)
    medit(3635)
        bash(3637)
        gnome-pty-helpe(3636)
    qutim(3349)
    ssh-agent(2738)
    x-www-browser(3058)
        plugin-containe(4623)
orage(2766)
pnmixer(2764)
polkit-gnome-au(2759)
pulseaudio(2795)
thunar(2760)
tilda(2770)
    bash(3637)
    gnome-pty-helpe(3636)
tint2(2761)
xbindkeys(2737)
xfce4-power-man(2786)
xfconfd(2793)
бог создал труд и обезьяну
чтоб получился человек
а вот пингвина он не трогал
тот сразу вышел хорошо

ypaseka
Сообщения: 10
Зарегистрирован: 20 янв 2015, 19:13
Темы: 2
Статус: Не в сети

Re: pstree.pl

Сообщение ypaseka » 23 янв 2015, 03:24

./pstree.pl --user=zen так у меня не запускается, только так
perl pstree.pl --user=zen, но выписывает почему то все процесы... юзера создавал через useradd

Аватара пользователя
ZEN
Администратор
Сообщения: 1350
Зарегистрирован: 27 сен 2012, 18:23
Темы: 206
Откуда: Украина, Одесса
Статус: Не в сети

Re: pstree.pl

Сообщение ZEN » 23 янв 2015, 12:43

Вечером буду дописывать поддержку GD, посмотрю в чем бага. Что правда у меня нормально работает.
Да, кстати, нового пользователя не обязательно было создавать. Можно было попробовать на текущем пользователе и на пользователе root :)
бог создал труд и обезьяну
чтоб получился человек
а вот пингвина он не трогал
тот сразу вышел хорошо

Аватара пользователя
ZEN
Администратор
Сообщения: 1350
Зарегистрирован: 27 сен 2012, 18:23
Темы: 206
Откуда: Украина, Одесса
Статус: Не в сети

Re: pstree.pl

Сообщение ZEN » 26 янв 2015, 00:51

Так.. багу не нашел, но вывод в картинку сделал. Собственно, скрипт теперь выводит текст либо в картинку, либо в текстовый файл.

Пришлось в качестве костыля сделать переменную $fh глобальной, так как оптимизировать нет ни желания, ни сил

Примеры запуска скрипта:

Код: Выделить всё

./pstree.pl --file=/tmp/test.txt # список процессов всех пользователей в текстовый файл
./pstree.pl --file=/tmp/image.png # список процессов всех пользователей в изображение
./pstree.pl --file=/tmp/image.png --user=root # список процессов root в png файл

Код: Выделить всё

#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw/any/;
use Getopt::Long;
use IO::File;
use GD::Simple;
use Switch;

use constant IMG_INDENT_LEFT => 10;
use constant IMG_LINE_HEIGHT => 10;
use constant IMG_WIDTH       => 400;

my $user = '';
my $file = undef;
my $is_image = 0;
my $line_counter = 0;
my $fh = undef;

main();

sub usage {
    print STDOUT "Usage:\n";
    print STDOUT "$0 --file=$ENV{HOME}/test.txt\n";
    print STDOUT "or\n";
    print STDOUT "$0 --file=$ENV{HOME}/image.png\n";
    print STDOUT "or\n";
    print STDOUT "$0 --file=$ENV{HOME}/image.png --user=$ENV{USER}\n";
}

sub main {

    GetOptions(
        "user=s" => \$user,
        "file=s" => \$file
    ) or usage() and exit 1;

    usage() && exit 2 unless ($file);

    my ($ext) = $file =~ /\.([^.]+)$/;
    switch ($ext) {
        case "txt" { $is_image = 0; }
        case "png" { $is_image = 1; }
        else {
            print STDERR "Unknown file extention\n";
            usage();
            exit 3;
        }
    }

    unless (-f $file) {
        my $f = new IO::File($file, "a");
        if (!$f) {
            print STDERR "Cannot open: $!\n";
            exit 4;
        }
        $f->close();
    }

    my (undef, undef, $uid) = getpwnam($user) if $user;
    if ($user && !defined $uid) {
        print STDERR "Unknown user - $user\n";
        exit 5;
    }

    opendir my $dh, "/proc" or die $!;
    my @pid_dirs = grep { m{^[1-9][0-9]*$} } readdir $dh;
    closedir $dh;

    my @program_infos;
    for my $pid (@pid_dirs) {
        my $program_info = read_program_status($pid);

        next unless defined $program_info;
        ($program_info->{uid}) = split( ' ', $program_info->{uid});
        next if ($user && $uid != $program_info->{uid});

        push @program_infos, $program_info;
    }

    if ($user && $uid) {
        # Fix non-existing ppid
        for my $pid (@program_infos) {
            if (!any { $_->{pid} == $pid->{ppid} } @program_infos) {
                $pid->{ppid} = 0;
            }
        }
    }

    my %pid_name;
    map { $pid_name{$_->{pid}} = $_->{name} } @program_infos;

    my %name_pid;
    map { $name_pid{$_->{name}} = $_->{pid} } @program_infos;

    my $ps_tree = {};
    my @parents;
    my @children;
    for my $program_info (@program_infos) {
        my $ppid = $program_info->{ppid};
    next if $ppid == 0 && !$user;

        my $pid = $program_info->{pid};
        $ps_tree->{$ppid}->{$pid} = 1;
    }

    my @ppids = keys %{$ps_tree};
    construct_ps_tree($ps_tree, $ps_tree, \@ppids);

    my $name_tree = {};
    ps_tree_to_name_tree($ps_tree, $name_tree, \%pid_name);

    my $output = '';
    open( $fh, ">", \$output ) or die("Cannot open: $!");
    print_tree($name_tree, \%name_pid, 0);
    close( $fh );

    open( $fh, ">", $file) or die("Cannot open: $!");

    if ($is_image) {
        binmode $fh;
        my $img = GD::Simple->new(IMG_WIDTH, ($line_counter * IMG_LINE_HEIGHT));
        my $start = 1;
        for(split(/\n/, $output)){
            $img->moveTo(IMG_INDENT_LEFT, (IMG_LINE_HEIGHT * $start++));
            $img->string($_);
        }
        print $fh $img->png;
    } else {
        print $fh $output;
    }

    close( $fh );
}

sub ps_tree_to_name_tree {
    my ($ps_tree, $name_tree, $pid_name) = @_;

    for my $pid (keys $ps_tree) {
        my $name = $pid_name->{$pid} || '0';
        if (ref $ps_tree->{$pid} eq "HASH") {
            $name_tree->{$name} = {};
            ps_tree_to_name_tree($ps_tree->{$pid}, $name_tree->{$name}, $pid_name);
        } else {
            $name_tree->{$name} = 1;
        }
    }
}

sub construct_ps_tree {
    my ($ps_tree, $parent, $ppids) = @_;

    my @deleted_pids;
    for my $pid (keys %{$parent}) {
        next unless ref $parent->{$pid} eq 'HASH';
        for my $child_pid (keys %{$parent->{$pid}}) {
            if (any { $_ == $child_pid } @{$ppids}) {
                $parent->{$pid}->{$child_pid} = $ps_tree->{$child_pid};
                push @deleted_pids, $child_pid;
                my @children = keys %{$ps_tree->{$child_pid}};
                construct_ps_tree($ps_tree, $parent->{$pid}->{$child_pid}, \@children);
            }
        }
    }

    for my $deleted_pid (@deleted_pids) {
        delete $ps_tree->{$deleted_pid};
    }
}

sub print_tree {
    my ($name_tree, $name_pid, $indent_level) = @_;

    my $indent = "    " x $indent_level;
    for my $name (sort keys %{$name_tree}) {
        my $pname = $name_pid->{$name} || $name;
        if ($pname == "0") {
            if (ref $name_tree->{$name} eq 'HASH') {
                print_tree($name_tree->{$name}, $name_pid, $indent_level);
            }
        } else {
            printf $fh "%s%s(%s)\n", $indent, $name, $name_pid->{$name} || $name;
            if (ref $name_tree->{$name} eq 'HASH') {
                print_tree($name_tree->{$name}, $name_pid, $indent_level+1);
            }
            $line_counter++;
        }
    }
}

sub read_program_status {
    my $pid = shift;

    my %program_info;
    open my $fh, '<', "/proc/$pid/status" or return;
    while (defined(my $line = <$fh>)) {
        chomp $line;

        if ($line =~ m{^([^:]+):\s*(.+)$}) {
            $program_info{lc $1} = $2;
        }
    }
    close $fh;

    return \%program_info;
}
бог создал труд и обезьяну
чтоб получился человек
а вот пингвина он не трогал
тот сразу вышел хорошо

ypaseka
Сообщения: 10
Зарегистрирован: 20 янв 2015, 19:13
Темы: 2
Статус: Не в сети

Re: pstree.pl

Сообщение ypaseka » 26 янв 2015, 18:36

после пробы инсталяции GD:Simple module выскочило

Код: Выделить всё

Running make install
Manifying 1 pod document
Manifying 1 pod document
Appending installation info to /usr/lib/perl/5.18/perllocal.pod
  ADAMK/Devel-Leak-Module-0.02.tar.gz
  /usr/bin/make install  -- OK
Failed during this command:
 LDS/GD-2.56.tar.gz                           : writemakefile NO '/usr/bin/perl Build.PL --installdirs site' returned status 512
что то видно не доинсталировалось. как заинсталировать нормально потому что

Код: Выделить всё

Can't locate GD/Simple.pm in @INC (you may need to install the GD::Simple module) 
:?

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 0 гостей