Handle hostnames with upper-case letters
[webmin.git] / lpadmin / linux-lib.pl
1 # linux-lib.pl
2 # Functions for printcap-style printer management
3
4 # list_printers()
5 # Returns a list of known printer names
6 sub list_printers
7 {
8 local($l, @rv);
9 foreach $l (&list_printcap()) {
10         $l->{'name'} =~ /^([^\|]+)/;
11         push(@rv, $1);
12         }
13 return @rv;
14 }
15
16 # get_printer(name, [nostatus])
17 sub get_printer
18 {
19 local($l, %prn, @w, @n, $w, %cap, @jobs);
20 foreach $l (&list_printcap()) {
21         @n = split(/\|/, $l->{'name'});
22         if ($n[0] eq $_[0]) {
23                 # found the printer.. get info from printcap
24                 $prn{'name'} = $n[0];
25                 if (@n > 2) { $prn{'alias'} = [ @n[1..$#n-1] ]; }
26                 if (@n > 1) { $prn{'desc'} = $n[$#n]; }
27                 $prn{'iface'} = $l->{'if'};
28                 $prn{'banner'} = !defined($l->{'sh'});
29                 $prn{'dev'} = $l->{'lp'};
30                 $prn{'rhost'} = $l->{'rm'};
31                 $prn{'rqueue'} = $l->{'rp'};
32                 $prn{'msize'} = $l->{'mx'};
33                 $prn{'comment'} = $l->{'comment'};
34                 $prn{'ro'} = $l->{'file'} eq $config{'ro_printcap_file'};
35
36                 if (!$_[1]) {
37                         # call lpc to get status
38                         local $esc = quotemeta($prn{'name'});
39                         $out = &backquote_command("lpc status $esc 2>&1", 1);
40                         $prn{'accepting'} = ($out =~ /queuing is enabled/);
41                         $prn{'enabled'} = ($out =~ /printing is enabled/);
42                         }
43
44                 return \%prn;
45                 }
46         }
47 return undef;
48 }
49
50 # get_jobs(printer)
51 sub get_jobs
52 {
53 local (@jobs, $htype);
54 local $esc = quotemeta($_[0]);
55 open(LPQ, "lpq -P$esc |");
56 while(<LPQ>) {
57         chop;
58         if (/^\s*Rank\s+Owner\s+\S+\s+Job/) { $htype = 2; }
59         elsif (/^Rank\s+Owner\s+/) { $htype = 1; }
60         elsif ($htype == 1 &&
61                /^(\S+)\s+(\S+)\s+(\d+)\s+(.*\S)\s+(\d+)\s+(\S+)$/) {
62                 # Normal lpq output
63                 local(%job, $f, @pq);
64                 $job{'id'} = $3;
65                 $job{'user'} = $2;
66                 $job{'size'} = $5;
67                 $job{'file'} = $4;
68                 $job{'printing'} = ($1 eq "active");
69                 local $d = "$config{'spool_dir'}/$_[0]";
70                 opendir(DIR, $d);
71                 while($f = readdir(DIR)) {
72                         if ($f =~ /df.(\d+)/ && $1 == $job{'id'}) {
73                                 push(@pq, "$d/$f");
74                                 }
75                         }
76                 closedir(DIR);
77                 $job{'printfile'} = @pq ? \@pq : undef;
78                 push(@jobs, \%job);
79                 }
80         elsif ($htype == 2 &&
81                /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d*)\s+(\S+)\s+(\S+)\s+(\S+)$/) {
82                 # PLP lpq output
83                 local(%job);
84                 $job{'id'} = $4;
85                 $job{'user'} = $2;
86                 $job{'size'} = $4;
87                 $job{'file'} = $6;
88                 push(@jobs, \%job);
89                 }
90         }
91 close(LPQ);
92 return @jobs;
93 }
94
95 sub printer_support
96 {
97 return $_[0] !~ /^(why|allow|default|ctype|sysv|direct|ipp)$/;
98 }
99
100
101 # create_printer(&details)
102 # Create a new printer in /etc/printcap
103 sub create_printer
104 {
105 local(%cap);
106 $cap{'sd'} = "$config{'spool_dir'}/$_[0]->{'name'}";
107 &lock_file($cap{'sd'});
108 mkdir($cap{'sd'}, 0755);
109 &unlock_file($cap{'sd'});
110
111 &lock_file($config{'printcap_file'});
112 local $lref = &read_file_lines($config{'printcap_file'});
113 push(@$lref, &make_printcap($_[0], \%cap));
114 &flush_file_lines($config{'printcap_file'});
115 &unlock_file($config{'printcap_file'});
116
117 &apply_status($_[0]);
118 }
119
120 # modify_printer(&details)
121 sub modify_printer
122 {
123 local(@old, $o, $old, @cap);
124 &lock_file($config{'printcap_file'});
125 @old = &list_printcap();
126 foreach $o (@old) {
127         $o->{'name'} =~ /^([^\|]+)/;
128         if ($1 eq $_[0]->{'name'}) {
129                 # found current details
130                 $old = $o;
131                 last;
132                 }
133         }
134 if (!$old) { &error("Printer '$_[0]->{'name'}' no longer exists"); }
135
136 &lock_file($config{'printcap_file'});
137 local $lref = &read_file_lines($config{'printcap_file'});
138 splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1,
139        &make_printcap($_[0], $old));
140 &flush_file_lines($config{'printcap_file'});
141 &unlock_file($config{'printcap_file'});
142
143 &apply_status($_[0]);
144 }
145
146 # delete_printer(name)
147 sub delete_printer
148 {
149 local(@old, $o, $old, @cap);
150 &lock_file($config{'printcap_file'});
151 @old = &list_printcap();
152 foreach $o (@old) {
153         $o->{'name'} =~ /^([^\|]+)/;
154         if ($1 eq $_[0]) {
155                 # found current details
156                 $old = $o;
157                 last;
158                 }
159         }
160 if (!$old) { &error("Printer '$_[0]' no longer exists"); }
161
162 &lock_file($config{'printcap_file'});
163 local $lref = &read_file_lines($config{'printcap_file'});
164 splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1);
165 &flush_file_lines($config{'printcap_file'});
166 &unlock_file($config{'printcap_file'});
167
168 if ($old->{'sd'} eq "$config{'spool_dir'}/$_[0]") {
169         system("rm -rf '$old->{'sd'}'");
170         }
171 }
172
173 # cancel_job(printer, job)
174 # Calls lprm to remove some job
175 sub cancel_job
176 {
177 local($out);
178 local $esc = quotemeta($_[0]);
179 local $iesc = quotemeta($_[1]);
180 $out = &backquote_logged("lprm -P$esc $iesc 2>&1");
181 if ($?) { &error("lprm failed : $out"); }
182 }
183
184 # make_printcap(&details, &old)
185 # Updates or creates a printcap line
186 sub make_printcap
187 {
188 local(%prn, %cap, $a, $rv, $c);
189 %prn = %{$_[0]}; %cap = %{$_[1]};
190 $cap{'if'} = $prn{'iface'} ? $prn{'iface'} : undef;
191 $cap{'sh'} = $prn{'banner'} ? undef : "";
192 $cap{'lp'} = $prn{'dev'} ? $prn{'dev'} : undef;
193 $cap{'rm'} = $prn{'rhost'} ? $prn{'rhost'} : undef;
194 $cap{'rp'} = $prn{'rqueue'} ? $prn{'rqueue'} : undef;
195 $cap{'mx'} = defined($prn{'msize'}) ? $prn{'msize'} : undef;
196 $rv = $prn{'comment'}."\n" if ($prn{'comment'});
197 $rv .= $prn{'name'};
198 foreach $a (@{$prn{'alias'}}) { $rv .= "|$a"; }
199 $rv .= "|$prn{'desc'}" if ($prn{'desc'});
200 foreach $c (keys %cap) {
201         if ($c =~ /^(\S\S)(#?)$/ && defined($cap{$c})) {
202                 if ($cap{$c} eq "") { $rv .= ":$c"; }
203                 elsif ($cap{$c} =~ /^\d+$/) { $rv .= ":$c#$cap{$c}"; }
204                 else { $rv .= ":$c=$cap{$c}"; }
205                 }
206         }
207 $rv .= ":";
208 return $rv;
209 }
210
211 # list_printcap()
212 # Returns an array of associative arrays containing printcap fields
213 sub list_printcap
214 {
215 return @list_printcap_cache if (scalar(@list_printcap_cache));
216 local(@rv, @line, @comment, @eline, @sline, $line, $cont, $lnum, $i,
217       %done, $capfile);
218 foreach $capfile ($config{'printcap_file'}, $config{'ro_printcap_file'}) {
219         next if (!$capfile || $done{$capfile}++);
220         open(CAP, $capfile);
221         $lnum = 0;
222         while($line = <CAP>) {
223                 $line =~ s/\s+$//g;     # remove trailing spaces/newline
224                 if ($line =~ /^(##.*)/) {
225                         # special commented line .. keep it
226                         $comment[@line] = $line;
227                         }
228                 else {
229                         $line =~ s/^#.*$//g;    # remove comments
230                         $line =~ s/^\s+//g;     # remove leading spaces
231                         if ($line =~ /\S/) {
232                                 local $ncont = ($line =~ s/\\$//g);
233                                 if ($cont) {
234                                         $line[$#line] .= $line;
235                                         $eline[@line - 1] = $lnum;
236                                         }
237                                 else {
238                                         push(@line, $line);
239                                         $eline[@line - 1] = $sline[@line - 1] = $lnum;
240                                         }
241                                 $cont = $ncont;
242                                 }
243                         else {
244                                 # only keep comments immediately before an entry
245                                 $comment[@line] = undef;
246                                 }
247                         }
248                 $lnum++;
249                 }
250         close(CAP);
251         for($i=0; $i<@line; $i++) {
252                 local(%cap);
253                 @w = split(/:+/, $line[$i]);
254                 $cap{'name'} = $w[0];
255                 $cap{'line'} = $sline[$i] - ($comment[$i] ? 1 : 0);
256                 $cap{'eline'} = $eline[$i];
257                 $cap{'comment'} = $comment[$i];
258                 $cap{'file'} = $capfile;
259                 foreach $w (@w[1..$#w]) {
260                         if ($w =~ /^([A-z0-9]+)[=#](.*)$/) { $cap{$1} = $2; }
261                         elsif ($w =~ /^([A-z0-9]+)$/) { $cap{$w} = ""; }
262                         }
263                 push(@rv, \%cap);
264                 }
265         }
266 @list_printcap_cache = @rv;
267 return @rv;
268 }
269
270 # apply_status(&details)
271 # Calls lpc to enable or disable a printer.
272 # Restarting lpd doesn't seem to be necessary?
273 sub apply_status
274 {
275 local($out);
276 local $esc = quotemeta($_[0]->{'name'});
277 $out = &backquote_command("lpc status $esc 2>&1", 1);
278 if ($_[0]->{'enabled'} && $out !~ /printing is enabled/)
279         { &backquote_logged("lpc up $esc"); }
280 elsif (!$_[0]->{'enabled'} && $out =~ /printing is enabled/)
281         { &backquote_logged("lpc down $esc"); }
282 if ($_[0]->{'accepting'} && $out !~ /queuing is enabled/)
283         { &backquote_logged("lpc enable $esc"); }
284 elsif (!$_[0]->{'accepting'} && $out =~ /queuing is enabled/)
285         { &backquote_logged("lpc disable $esc"); }
286 }
287
288 # sched_running()
289 # Returns the pid if lpsched is running, 0 if not, -1 if cannot be stopped
290 sub sched_running
291 {
292 @pid = &find_byname("lpd");
293 if (@pid) { return $pid[0]; }
294 return 0;
295 }
296
297 # start_sched()
298 # Start lpsched
299 sub start_sched
300 {
301 local $out = &backquote_logged("lpd 2>&1");
302 if ($?) { &error("failed to start lpd : <tt>$out</tt>"); }
303 }
304
305 # stop_sched(pid)
306 # Stop the running lpsched process
307 sub stop_sched
308 {
309 local @pid = &find_byname("lpd");
310 &kill_logged('TERM', @pid) || &error("Failed to stop lpd : $!");
311 }
312
313 # print_command(printer, file)
314 # Returns the command to print some file on some printer
315 sub print_command
316 {
317 local $esc = quotemeta($_[0]);
318 local $fesc = quotemeta($_[1]);
319 return "lpr -P$esc $fesc";
320 }
321
322 # check_print_system()
323 sub check_print_system
324 {
325 &has_command("lpr") || return &text('linux_ecmd', "<tt>lpr</tt>");
326 -d $config{'spool_dir'} || return &text('linux_espool', "<tt>$config{'spool_dir'}</tt>");
327 return undef;
328 }
329
330 if (-r "/dev/lp0") {
331         @device_files = ("/dev/lp0", "/dev/lp1", "/dev/lp2", "/dev/lp3",
332                          "/dev/ttyS0", "/dev/ttyS1", "/dev/null");
333         }
334 else {
335         @device_files = ("/dev/lp1", "/dev/lp2", "/dev/lp2", "/dev/lp3",
336                          "/dev/ttyS0", "/dev/ttyS1", "/dev/null");
337         }
338 if (-r "/dev/usblp0") {
339         push(@device_files, "/dev/usblp0", "/dev/usblp1",
340                             "/dev/usblp2", "/dev/usblp3");
341         }
342 elsif (-r "/dev/usb/lp0") {
343         push(@device_files, "/dev/usb/lp0", "/dev/usb/lp1",
344                             "/dev/usb/lp2", "/dev/usb/lp3");
345         }
346 @device_names = (&text('linux_paralel', "1"), &text('linux_paralel', "2"),
347                  &text('linux_paralel', "3"), &text('linux_paralel', "4"),
348                  &text('linux_serial', "1"), &text('linux_serial', "2"),
349                  $text{'linux_null'}, &text('linux_usb', 1),
350                  &text('linux_usb', 2), &text('linux_usb', 3),
351                  &text('linux_usb', 4));
352