Handle hostnames with upper-case letters
[webmin.git] / proc / sysv-lib.pl
1 # sysv-lib.pl
2 # Functions for parsing sysv-style ps output
3
4 $has_stime = $gconfig{'os_type'} eq 'solaris';
5 $has_task = $gconfig{'os_type'} eq 'solaris' && $gconfig{'os_version'} >= 10;
6 $has_zone = $gconfig{'os_type'} eq 'solaris' && $gconfig{'os_version'} >= 10;
7
8 # list_processes([pid]*)
9 sub list_processes
10 {
11 local($line, $dummy, @w, $i, $_, $pcmd, @plist);
12 foreach (@_) { $pcmd .= " -p $_"; }
13 if (!$pcmd) { $pcmd = " -e"; }
14 $ENV{'COLUMNS'} = 10000;        # needed on AIX
15 local @cols = ( "user","ruser","group","rgroup","pid","ppid","pgid","pcpu","vsz",
16                 "nice","etime","time",
17                 ($has_stime ? ("stime") : ( )),
18                 ($has_task ? ("taskid") : ( )),
19                 ($has_zone ? ("zone") : ( )),
20                 "tty","args" );
21 open(PS, "ps -o ".join(",", @cols)." $pcmd |");
22 $dummy = <PS>;
23 for($i=0; $line=<PS>; $i++) {
24         chop($line);
25         $line =~ s/^\s+//g;
26         @w = split(/\s+/, $line);
27         if ($line =~ /ps -o user,ruser/) {
28                 # Skip ps command
29                 $i--; next;
30                 }
31         $plist[$i]->{"pid"} = $w[4];
32         $plist[$i]->{"ppid"} = $w[5];
33         $plist[$i]->{"user"} = $w[0];
34         $plist[$i]->{"cpu"} = "$w[7] %";
35         $plist[$i]->{"size"} = "$w[8] kB";
36         local $ofs = 0;
37         if ($has_stime) {
38                 $plist[$i]->{"_stime"} = $w[12+$ofs];
39                 $plist[$i]->{"_stime"} =~ s/_/ /g;
40                 $ofs++;
41                 }
42         if ($has_task) {
43                 $plist[$i]->{"_task"} = $w[12+$ofs];
44                 $ofs++;
45                 }
46         if ($has_zone) {
47                 $plist[$i]->{"_zone"} = $w[12+$ofs];
48                 $ofs++;
49                 }
50         $plist[$i]->{"time"} = $w[11];
51         $plist[$i]->{"nice"} = $w[9] =~ /\d+/ ? $w[9]-20 : $w[9];
52         $plist[$i]->{"args"} = @w<14+$ofs ? "defunct"
53                                           : join(' ', @w[13+$ofs..$#w]);
54         $plist[$i]->{"_group"} = $w[2];
55         $plist[$i]->{"_ruser"} = $w[1];
56         $plist[$i]->{"_rgroup"} = $w[3];
57         $plist[$i]->{"_pgid"} = $w[6];
58         $plist[$i]->{"_tty"} = $w[12+$ofs] =~ /\?/ ? $text{'edit_none'}
59                                                    : "/dev/$w[12+$ofs]";
60         }
61 close(PS);
62 return @plist;
63 }
64
65 # find_mount_processes(mountpoint)
66 # Find all processes under some mount point
67 sub find_mount_processes
68 {
69 local($out);
70 $out = `fuser -c $_[0] 2>/dev/null`;
71 $out =~ s/^\s+//g; $out =~ s/\s+$//g;
72 return split(/\s+/, $out);
73 }
74
75 # find_file_processes([file]+)
76 # Find all processes with some file open
77 sub find_file_processes
78 {
79 local($out, $files);
80 $files = join(' ', map { quotemeta($_) } map { glob($_) } @_);
81 $out = &backquote_command("fuser $files 2>/dev/null");
82 $out =~ s/^\s+//g; $out =~ s/\s+$//g;
83 return split(/\s+/, $out);
84 }
85
86 # renice_proc(pid, nice)
87 sub renice_proc
88 {
89 return undef if (&is_readonly_mode());
90 local $out = &backquote_logged("renice $_[1] -p $_[0] 2>&1");
91 if ($?) { return $out; }
92 return undef;
93 }
94
95 # get_new_pty()
96 # Returns the filehandles and names for a pty and tty
97 sub get_new_pty
98 {
99 if (!-e "/dev/ptyp0") {
100         # Must use IO::Pty :(
101         &error("IO::Pty Perl module is not installed");
102         }
103 else {
104         # Need to search through pty files
105         opendir(DEV, "/dev");
106         local @ptys = map { "/dev/$_" } (grep { /^pty/ } readdir(DEV));
107         closedir(DEV);
108         local ($pty, $tty);
109         foreach $pty (@ptys) {
110                 open(PTY, "+>$pty") || next;
111                 local $tty = $pty; $tty =~ s/pty/tty/;
112                 open(TTY, "+>$tty") || next;
113                 local $old = select(PTY); $| = 1;
114                 select(TTY); $| = 1; select($old);
115                 return (*PTY, *TTY, $pty, $tty);
116                 }
117         return ();
118         }
119 }
120
121 $has_trace_command = $gconfig{'os_type'} eq 'solaris' &&
122                      &has_command("truss");
123
124 # open_process_trace(pid, [&syscalls])
125 # Starts tracing on some process, and returns a trace object
126 sub open_process_trace
127 {
128 local $fh = time().$$;
129 local $sc;
130 if (@{$_[1]}) {
131         $sc = "-t ".join(",", @{$_[1]});
132         }
133 local $tpid = open($fh, "truss $sc -i -p $_[0] 2>&1 |");
134 $line = <$fh>;
135 return { 'pid' => $_[0],
136          'tpid' => $tpid,
137          'fh' => $fh };
138 }
139
140 # close_process_trace(&trace)
141 # Halts tracing on some trace object
142 sub close_process_trace
143 {
144 kill('TERM', $_[0]->{'tpid'}) if ($_[0]->{'tpid'});
145 close($_[0]->{'fh'});
146 }
147
148 # read_process_trace(&trace)
149 # Returns an action structure representing one action by traced process, or
150 # undef if an error occurred
151 sub read_process_trace
152 {
153 local $fh = $_[0]->{'fh'};
154 local @tm = localtime(time());
155 while(1) {
156         local $line = <$fh>;
157         return undef if (!$line);
158         if ($line =~ /^([^\(]+)\((.*)\)(\s*=\s*(\-?\d+)|\s+(Err\S+))?/) {
159                 local $action = { 'time' => time(),
160                                   'call' => $1,
161                                   'rv' => $4 ne "" ? $4 : $5 };
162                 local $args = $2;
163                 local @args;
164                 while(1) {
165                         if ($args =~ /^[ ,]*(\{[^}]*\})(.*)$/) {
166                                 # A structure in { }
167                                 push(@args, $1);
168                                 $args = $2;
169                                 }
170                         elsif ($args =~ /^[ ,]*"([^"]*)"\.*(.*)$/) {
171                                 # A quoted string
172                                 push(@args, $1);
173                                 $args = $2;
174                                 }
175                         elsif ($args =~ /^[ ,]*\[([^\]]*)\](.*)$/) {
176                                 # A square-bracket number
177                                 push(@args, $1);
178                                 $args = $2;
179                                 }
180                         elsif ($args =~ /^[ ,]*\<([^\>]*)\>(.*)$/) {
181                                 # An angle-bracketed string
182                                 push(@args, $1);
183                                 $args = $2;
184                                 }
185                         elsif ($args =~ /[ ,]*([^, ]+)(.*)$/) {
186                                 # Just a number
187                                 push(@args, $1);
188                                 $args = $2;
189                                 }
190                         else {
191                                 last;
192                                 }
193                         }
194                 $action->{'args'} = \@args;
195                 return $action;
196                 }
197         }
198 }
199
200 # os_get_cpu_info()
201 # Returns a list containing the 5, 10 and 15 minute load averages
202 sub os_get_cpu_info
203 {
204 local $out = `uptime 2>&1`;
205 if ($out =~ /load average:\s+(\S+),\s+(\S+),\s+(\S+)/) {
206         return ($1, $2, $3);
207         }
208 else {
209         return ( );
210         }
211 }
212
213 # get_memory_info()
214 # Returns a list containing the real mem, free real mem, swap and free swap
215 # (In kilobytes).
216 sub get_memory_info
217 {
218 if (!&has_command("kstat")) {
219         return ( );
220         }
221 local %stat;
222 foreach my $s ("physmem", "freemem") {
223         local $out = &backquote_command("kstat -p -m unix -s $s");
224         if ($out =~ /\s+(\d+)/) {
225                 $stat{$s} = $1;
226                 }
227         }
228 local ($swaptotal, $swapfree);
229 &open_execute_command(SWAP, "swap -l", 1);
230 while(<SWAP>) {
231         if (/^\S+\s+\d+,\d+\s+\d+\s+(\d+)\s+(\d+)/) {
232                 $swaptotal += $1;
233                 $swapfree += $2;
234                 }
235         }
236 close(SWAP);
237 local $pagesize = &backquote_command("pagesize 2>/dev/null");
238 $pagesize = int($pagesize)/1024;
239 $pagesize ||= 8;        # Fallback
240 return ($stat{'physmem'}*$pagesize, $stat{'freemem'}*$pagesize,
241         $swaptotal/2, $swapfree/2);
242 }
243
244
245 foreach $ia (keys %text) {
246         if ($ia =~ /^sysv(_\S+)/) {
247                 $info_arg_map{$1} = $text{$ia};
248                 }
249         }
250 delete($info_arg_map{'_stime'}) if (!$has_stime);
251
252 @nice_range = (-20 .. 19);
253
254 $has_fuser_command = 1;
255
256 1;
257