Handle hostnames with upper-case letters
[webmin.git] / ppp-client / ppp-client-lib.pl
1 # ppp-client-lib.pl
2 # Common functions for configuring WV-Dial
3 # XXX what about redhat connect process?
4 # XXX what about SuSE connect process?
5
6 BEGIN { push(@INC, ".."); };
7 use WebminCore;
8 &init_config();
9 $details_file = "$module_config_directory/connect";
10 $resolv_conf = "/etc/resolv.conf";
11 $ppp_resolv_conf = "/etc/ppp/resolv.conf";
12 $save_resolv_conf = $resolv_conf.".save";
13
14 # get_config()
15 # Returns a list of all configuration settings
16 sub get_config
17 {
18 local (@rv, $sect);
19 local $lnum = 0;
20 open(FILE, $config{'file'});
21 while(<FILE>) {
22         s/^\s*;.*//;
23         s/\r|\n//g;
24         if (/^\s*\[(.*)\]/) {
25                 # Start of a section
26                 $sect = { 'name' => $1,
27                           'index' => scalar(@rv),
28                           'line' => $lnum,
29                           'eline' => $lnum,
30                           'values' => { },
31                           'onames' => { } };
32                 push(@rv, $sect);
33                 }
34         elsif (/^\s*([^=]+\S)\s*=\s*(.*)/ && $sect) {
35                 # A directive within a section
36                 $sect->{'values'}->{lc($1)} = $2;
37                 $sect->{'onames'}->{lc($1)} = $1;
38                 $sect->{'eline'} = $lnum;
39                 }
40         $lnum++;
41         }
42 close(FILE);
43 return \@rv;
44 }
45
46 # create_dialer(&dialer)
47 # Add a dialer to the configuration
48 sub create_dialer
49 {
50 local $lref = &read_file_lines($config{'file'});
51 push(@$lref, "", &dialer_lines($_[0]));
52 &flush_file_lines();
53 }
54
55 # update_dialer(&dialer)
56 sub update_dialer
57 {
58 local $lref = &read_file_lines($config{'file'});
59 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
60        &dialer_lines($_[0]));
61 &flush_file_lines();
62 }
63
64 # delete_dialer(&dialer)
65 sub delete_dialer
66 {
67 local $lref = &read_file_lines($config{'file'});
68 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1);
69 if ($_[0]->{'line'} && $lref->[$_[0]->{'line'}-1] eq "") {
70         splice(@$lref, $_[0]->{'line'}-1, 1);
71         }
72 &flush_file_lines();
73 }
74
75 # dialer_lines(&dialer)
76 sub dialer_lines
77 {
78 local @rv = "[$_[0]->{'name'}]";
79 local $k;
80 foreach $k (keys %{$_[0]->{'values'}}) {
81         local $pk = $_[0]->{'onames'}->{$k} || $k;
82         push(@rv, $pk." = ".$_[0]->{'values'}->{$k});
83         }
84 return @rv;
85 }
86
87 # device_name(device)
88 # Returns a human-readable modem device name
89 sub device_name
90 {
91 if ($_[0] =~ /^\/dev\/ttyS(\d+)$/) {
92         return &text('device_serial', $1+1);
93         }
94 else {
95         return "<tt>$_[0]</tt>";
96         }
97 }
98
99 # save_connect_details(ip, pid, section)
100 sub save_connect_details
101 {
102 &open_tempfile(DETAILS, ">$details_file");
103 &print_tempfile(DETAILS, $_[0],"\n",$_[1],"\n",$_[2],"\n");
104 &close_tempfile(DETAILS);
105 }
106
107 # get_connect_details()
108 sub get_connect_details
109 {
110 local ($ip, $pid, $sect);
111 open(DETAILS, $details_file) || return ();
112 chop($ip = <DETAILS>);
113 chop($pid = <DETAILS>);
114 chop($sect = <DETAILS>);
115 close(DETAILS);
116 return ($ip, $pid, $sect);
117 }
118
119 # get_wvdial_pid()
120 # Returns the PID of a running wvdial process
121 sub get_wvdial_pid
122 {
123 local $p;
124 &foreign_require("proc", "proc-lib.pl");
125 foreach $p (&proc::list_processes()) {
126         if ($p->{'args'} =~ /^\S*wvdial($|\s)/ && $p->{'args'} !~ /\[/) {
127                 return $p->{'pid'};
128                 }
129         }
130 return undef;
131 }
132
133 sub dialer_name
134 {
135 return $_[0] =~ /^Dialer Defaults$/i ?
136                 $text{'index_defaults'} :
137        $_[0] =~ /^Dialer\s+(.*)$/i ?
138                 &text('index_dialer', "$1") : $_[0];
139 }
140
141 # ppp_connect(section-name, textmode)
142 sub ppp_connect
143 {
144 # Get this dialer's configuration
145 local $conf = &get_config();
146 local ($dialer) = grep { lc($_->{'name'}) eq lc($_[0]) } @$conf;
147 local ($ddialer) = grep { lc($_->{'name'}) eq 'dialer defaults' } @$conf;
148 local ($inherits, $parent, $autodns);
149 if ($inherits = $dialer->{'values'}->{'inherits'}) {
150         ($parent) = grep { lc($_->{'name'}) eq lc($inherits) } @$conf;
151         }
152 $autodns = $dialer->{'values'}->{'auto dns'} ?
153                 ($dialer->{'values'}->{'auto dns'} =~ /on|yes|1/ ? 1 : 0) :
154            $parent->{'values'}->{'auto dns'} ?
155                 ($parent->{'values'}->{'auto dns'} =~ /on|yes|1/ ? 1 : 0) :
156            $ddialer->{'values'}->{'auto dns'} ?
157                 ($ddialer->{'values'}->{'auto dns'} =~ /on|yes|1/ ? 1 : 0) :
158                 1;
159
160 # Run wvdial, writing to a temp file in the background
161 local $stime = time();
162 local $sect = $_[0];
163 $sect =~ s/^Dialer\s+//;
164 local $temp = &tempname();
165 if (!($pid = fork())) {
166         untie(*STDIN);
167         untie(*STDOUT);
168         untie(*STDERR);
169         open(STDOUT, ">$temp");
170         open(STDERR, ">&STDOUT");
171         open(STDIN, "/dev/null");
172         exec($config{'wvdial'}, $sect);
173         exit(1);
174         }
175 while (!open(TEMP, $temp)) { }
176 &additional_log("exec", undef, "$config{'wvdial'} $sect");
177 unlink($temp);
178
179 # Read output until success or failure
180 if ($_[1] == 1) {
181         print &text('connect_cmd', "$config{'wvdial'} $sect"),"\n\n";
182         }
183 elsif ($_[1] == 0) {
184         print "<b>",&text('connect_cmd',
185                              "<tt>$config{'wvdial'} $sect</tt>"),"</b><br>\n";
186         print "<pre>";
187         }
188 local ($connected, $failed);
189 while(1) {
190         local $line = &wait_for_line();
191         if ($_[1] == 1) {
192                 print $line;
193                 }
194         elsif ($_[1] == 0) {
195                 print &html_escape($line);
196                 }
197         if ($line =~ /IP\s+address\s+is\s+(\d+\.\d+\.\d+\.\d+)/i) {
198                 # Connected OK!
199                 $connected = $1 eq "0.0.0.0" ? "*" : $1;
200                 last;
201                 }
202         elsif ($line =~ /starting\s+ppp/i) {
203                 # Connected in stupid mode
204                 $connected = "*";
205                 last;
206                 }
207         elsif (!$line) {
208                 # Program terminated .. must have failed!
209                 $failed++;
210                 last;
211                 }
212         }
213 print "</pre>\n" if ($_[1] == 0);
214
215 # If OK, save the PID for later
216 if ($connected) {
217         if ($_[1] == 0) {
218                 print "<b>",$connected eq '*' ? $text{'connect_noip'} :
219                         &text('connect_ip', "<tt>$connected</tt>"),"</b><p>\n";
220                 }
221         elsif ($_[1] == 1) {
222                 print $connected eq '*' ? $text{'connect_noip'} :
223                         &text('connect_ip', $connected),"\n";
224                 }
225         &save_connect_details($connected, $pid, $_[0]);
226         }
227 else {
228         if ($_[1] == 0) {
229                 print "<b>$text{'connect_failed'}</b><p>\n";
230                 }
231         elsif ($_[1] == 1) {
232                 print "$text{'connect_failed'}\n\n";
233                 }
234         }
235 $config{'dialer'} = $_[0];
236 &lock_file("$module_config_directory/config");
237 &write_file("$module_config_directory/config", \%config);
238 &unlock_file("$module_config_directory/config");
239
240 if ($connected && $autodns) {
241         # If the resolv.conf file has not been modified, and the PPP
242         # resolv.conf has, copy it into place
243         while(1) {
244                 sleep(3);
245                 local ($ip, $pid, $sect) = &get_connect_details();
246                 if (!$pid || !kill(0, $pid)) {
247                         # Connection is down .. DNS will never be updated
248                         if ($_[1] == 0) {
249                                 print "<b>$text{'connect_dnsdown'}</b><p>\n";
250                                 }
251                         elsif ($_[1] == 1) {
252                                 print "$text{'connect_dnsdown'}\n\n";
253                                 }
254                         last;
255                         }
256                 $now = time();
257                 if ($now > $stime+60) {
258                         # Took too long to update DNS
259                         if ($_[1] == 0) {
260                                 print "<b>$text{'connect_dnsto'}</b><p>\n";
261                                 }
262                         elsif ($_[1] == 1) {
263                                 print "$text{'connect_dnsto'}\n\n";
264                                 }
265                         last;
266                         }
267                 local @pst = stat($ppp_resolv_conf);
268                 local @ost = stat($resolv_conf);
269                 if ($ost[9] >= $stime) {
270                         # Something else has update the DNS config ..
271                         if ($_[1] == 0) {
272                                 print "<b>$text{'connect_dns2'}</b><p>\n";
273                                 }
274                         elsif ($_[1] == 1) {
275                                 print "$text{'connect_dns2'}\n\n";
276                                 }
277                         last;
278                         }
279                 if ($pst[9] >= $stime) {
280                         # A PPP DNS config has been created .. use it
281                         &system_logged("cp $resolv_conf $save_resolv_conf")
282                                 if (!-l $resolv_conf);
283                         unlink($resolv_conf);
284                         &system_logged("cp $ppp_resolv_conf $resolv_conf");
285                         if ($_[1] == 0) {
286                                 print "<b>$text{'connect_dns'}</b><p>\n";
287                                 }
288                         elsif ($_[1] == 1) {
289                                 print "$text{'connect_dns'}\n\n";
290                                 }
291                         last;
292                         }
293                 }
294         }
295 return $connected;
296 }
297
298 # wait_for_line()
299 # Reads a line from the temp file, or waits if there is no more to read. Only
300 # returns undef if the process has died
301 sub wait_for_line
302 {
303 local $line = <TEMP>;
304 return $line if ($line);
305 waitpid($pid, 1);
306 return undef if (!kill(0, $pid));
307 sleep(1);
308 return &wait_for_line();
309 }
310
311 # ppp_disconnect(mode, text-mode)
312 # Shuts down the active PPP connection
313 sub ppp_disconnect
314 {
315 local ($ip, $pid, $sect);
316 if ($_[0] == 0) {
317         ($ip, $pid, $sect) = &get_connect_details();
318         }
319 else {
320         $pid = &get_wvdial_pid();
321         }
322 if ($pid && &kill_logged('TERM', $pid)) {
323         # Tell the user that the connection is now down
324         if ($ip) {
325                 if ($_[1] == 0) {
326                         print &text('disc_ok1', "<tt>$ip</tt>", &dialer_name($sect)),"<p>\n";
327                         }
328                 elsif ($_[1] == 1) {
329                         print &text('disc_ok1', $ip, &dialer_name($sect)),"\n\n";
330                         }
331                 }
332         else {
333                 if ($_[1] == 0) {
334                         print $text{'disc_ok2'},"<p>\n";
335                         }
336                 elsif ($_[1] == 1) {
337                         print $text{'disc_ok2'},"\n\n";
338                         }
339                 }
340
341         # Restore the saved DNS config file, if it hasn't been done
342         sleep(3);
343         local @ost = stat($resolv_conf);
344         if (!-l $resolv_conf && $ost[9] < time()-5 && -r $save_resolv_conf) {
345                 &system_logged("mv $save_resolv_conf $resolv_conf");
346                 if ($_[1] == 0) {
347                         print "$text{'disc_dns'}<p>\n";
348                         }
349                 elsif ($_[1] == 1) {
350                         print "$text{'disc_dns'}\n\n";
351                         }
352                 }
353         &system_logged("rm -f $ppp_resolv_conf");
354         return 1;
355         }
356 else {
357         # Wasn't even active .. tell the user
358         if ($_[1] == 0) {
359                 print "$text{'disc_edown'}<p>\n";
360                 }
361         elsif ($_[1] == 1) {
362                 print "$text{'disc_edown'}\n\n";
363                 }
364         return 0;
365         }
366 }
367
368 1;
369