7bf76db90ffe8f49a3f5391f3cfc45822474a336
[racktables] / wwwroot / inc / remote.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 # The array below maps availability of particular commands for each particular
8 # device breed. Array values are functions implemented in deviceconfig.php, which
9 # is not normally included until any of the functions is actually called.
10 $breedfunc = array
11 (
12 'ios12-getcdpstatus-main' => 'ios12ReadCDPStatus',
13 'ios12-getlldpstatus-main' => 'ios12ReadLLDPStatus',
14 'ios12-get8021q-main' => 'ios12ReadVLANConfig',
15 'ios12-get8021q-swports' => 'ios12ReadSwitchPortList',
16 'ios12-get8021q-top' => 'ios12ScanTopLevel',
17 'ios12-get8021q-readport' => 'ios12PickSwitchportCommand',
18 'ios12-get8021q-readvlan' => 'ios12PickVLANCommand',
19 'ios12-getportstatus-main' => 'ciscoReadInterfaceStatus',
20 'ios12-getmaclist-main' => 'ios12ReadMacList',
21 'ios12-xlatepushq-main' => 'ios12TranslatePushQueue',
22 'ios12-getallconf-main' => 'ios12SpotConfigText',
23 'fdry5-get8021q-main' => 'fdry5ReadVLANConfig',
24 'fdry5-get8021q-top' => 'fdry5ScanTopLevel',
25 'fdry5-get8021q-readvlan' => 'fdry5PickVLANSubcommand',
26 'fdry5-get8021q-readport' => 'fdry5PickInterfaceSubcommand',
27 'fdry5-xlatepushq-main' => 'fdry5TranslatePushQueue',
28 'fdry5-getallconf-main' => 'fdry5SpotConfigText',
29 'vrp53-getlldpstatus-main' => 'vrp5xReadLLDPStatus',
30 'vrp53-get8021q-main' => 'vrp53ReadVLANConfig',
31 'vrp53-get8021q-top' => 'vrp53ScanTopLevel',
32 'vrp53-get8021q-readport' => 'vrp53PickInterfaceSubcommand',
33 'vrp53-getportstatus-main' => 'vrpReadInterfaceStatus',
34 'vrp53-getmaclist-main' => 'vrp53ReadMacList',
35 'vrp53-xlatepushq-main' => 'vrp53TranslatePushQueue',
36 'vrp53-getallconf-main' => 'vrp5xSpotConfigText',
37 'vrp55-getlldpstatus-main' => 'vrp5xReadLLDPStatus',
38 'vrp55-get8021q-main' => 'vrp55Read8021QConfig',
39 'vrp55-getportstatus-main' => 'vrpReadInterfaceStatus',
40 'vrp55-getmaclist-main' => 'vrp55ReadMacList',
41 'vrp55-xlatepushq-main' => 'vrp55TranslatePushQueue',
42 'vrp55-getallconf-main' => 'vrp5xSpotConfigText',
43 'nxos4-getcdpstatus-main' => 'ios12ReadCDPStatus',
44 'nxos4-getlldpstatus-main' => 'nxos4ReadLLDPStatus',
45 'nxos4-get8021q-main' => 'ios12ReadVLANConfig',
46 'nxos4-getportstatus-main' => 'ciscoReadInterfaceStatus',
47 'nxos4-getmaclist-main' => 'nxos4ReadMacList',
48 'nxos4-xlatepushq-main' => 'nxos4TranslatePushQueue',
49 'nxos4-getallconf-main' => 'nxos4SpotConfigText',
50 'dlink-get8021q-main' => 'dlinkReadVLANConfig',
51 'dlink-get8021q-top' => 'dlinkScanTopLevel',
52 'dlink-get8021q-pickvlan' => 'dlinkPickVLANCommand',
53 'dlink-getportstatus-main' => 'dlinkReadInterfaceStatus',
54 'dlink-getmaclist-main' => 'dlinkReadMacList',
55 'dlink-xlatepushq-main' => 'dlinkTranslatePushQueue',
56 'linux-get8021q-main' => 'linuxReadVLANConfig',
57 'linux-getportstatus-main' => 'linuxReadInterfaceStatus',
58 'linux-getmaclist-main' => 'linuxReadMacList',
59 'linux-xlatepushq-main' => 'linuxTranslatePushQueue',
60 'xos12-getlldpstatus-main' => 'xos12ReadLLDPStatus',
61 'xos12-get8021q-main' => 'xos12Read8021QConfig',
62 'xos12-xlatepushq-main' => 'xos12TranslatePushQueue',
63 'xos12-getallconf-main' => 'xos12SpotConfigText',
64 'jun10-get8021q-main' => 'jun10Read8021QConfig',
65 'jun10-xlatepushq-main' => 'jun10TranslatePushQueue',
66 'jun10-getallconf-main' => 'jun10SpotConfigText',
67 'jun10-getlldpstatus-main' => 'jun10ReadLLDPStatus',
68 'ftos8-xlatepushq-main' => 'ftos8TranslatePushQueue',
69 'ftos8-getlldpstatus-main' => 'ftos8ReadLLDPStatus',
70 'ftos8-getmaclist-main' => 'ftos8ReadMacList',
71 'ftos8-getportstatus-main' => 'ftos8ReadInterfaceStatus',
72 'ftos8-get8021q-main' => 'ftos8Read8021QConfig',
73 'ftos8-getallconf-main' => 'ftos8SpotConfigText',
74 'air12-xlatepushq-main' => 'air12TranslatePushQueue',
75 'air12-getallconf-main' => 'ios12SpotConfigText',
76 'eos4-getallconf-main' => 'eos4SpotConfigText',
77 'eos4-getmaclist-main' => 'eos4ReadMacList',
78 'eos4-getportstatus-main' => 'eos4ReadInterfaceStatus',
79 'eos4-getlldpstatus-main' => 'eos4ReadLLDPStatus',
80 'eos4-get8021q-main' => 'eos4Read8021QConfig',
81 'eos4-xlatepushq-main' => 'eos4TranslatePushQueue',
82 'ros11-getallconf-main' => 'ros11SpotConfigText',
83 'ros11-xlatepushq-main' => 'ros11TranslatePushQueue',
84 'ros11-getlldpstatus-main' => 'ros11ReadLLDPStatus',
85 'ros11-getportstatus-main' => 'ros11ReadInterfaceStatus',
86 'ros11-getmaclist-main' => 'ros11ReadMacList',
87 'ros11-get8021q-main' => 'ros11Read8021QConfig',
88 'ros11-get8021q-scantop' => 'ros11Read8021QScanTop',
89 'ros11-get8021q-vlandb' => 'ros11Read8021QVLANDatabase',
90 'ros11-get8021q-readports' => 'ros11Read8021QPorts',
91 'iosxr4-xlatepushq-main' => 'iosxr4TranslatePushQueue',
92 'iosxr4-getallconf-main' => 'iosxr4SpotConfigText',
93 'iosxr4-getlldpstatus-main'=> 'iosxr4ReadLLDPStatus',
94 'ucs-xlatepushq-main' => 'ucsTranslatePushQueue',
95 'ucs-getinventory-main' => 'ucsReadInventory',
96 );
97
98 define ('MAX_GW_LOGSIZE', 1024*1024); // do not store more than 1 MB of log data
99
100 $breed_by_swcode = array
101 (
102 244 => 'ios12', // IOS 12.0
103 251 => 'ios12', // IOS 12.1
104 252 => 'ios12', // IOS 12.2
105 254 => 'ios12', // IOS 12.0 (router OS)
106 255 => 'ios12', // IOS 12.1 (router OS)
107 256 => 'ios12', // IOS 12.2 (router OS)
108 257 => 'ios12', // IOS 12.3 (router OS)
109 258 => 'ios12', // IOS 12.4 (router OS)
110 1901 => 'ios12', // IOS 15.0
111 1963 => 'ios12', // IOS 15.1 (router OS)
112 963 => 'nxos4', // NX-OS 4.0
113 964 => 'nxos4', // NX-OS 4.1
114 1365 => 'nxos4', // NX-OS 4.2
115 1410 => 'nxos4', // NX-OS 5.0
116 1411 => 'nxos4', // NX-OS 5.1
117 1809 => 'nxos4', // NX-OS 5.2
118 1643 => 'nxos4', // NX-OS 6.0
119 1352 => 'xos12', // Extreme XOS 12
120 1360 => 'vrp53', // Huawei VRP 5.3
121 1361 => 'vrp55', // Huawei VRP 5.5
122 1369 => 'vrp55', // Huawei VRP 5.7
123 1363 => 'fdry5', // IronWare 5
124 1367 => 'jun10', // 10S
125 1597 => 'jun10', // 10R
126 1598 => 'jun10', // 11R
127 1599 => 'jun10', // 12R
128 1594 => 'ftos8', // Force10 FTOS 8
129 1673 => 'air12', // AIR IOS 12.3
130 1674 => 'air12', // AIR IOS 12.4
131 1675 => 'eos4', // Arista EOS 4
132 1759 => 'iosxr4', // Cisco IOS XR 4.2
133 1786 => 'ros11', // Marvell ROS 1.1
134
135 //... linux items added by the loop below
136 );
137
138 $breed_by_hwcode = array (
139 //... dlink items added by the loop below
140 );
141
142 $breed_by_mgmtcode = array (
143 1788 => 'ucs',
144 );
145
146 // add 'linux' items into $breed_by_swcode
147 $linux_sw_ranges = array (
148 225,235,
149 418,436,
150 1331,1334,
151 1395,1396,
152 1417,1422,
153 );
154 for ($i = 0; $i + 1 < count ($linux_sw_ranges); $i += 2)
155 for ($j = $linux_sw_ranges[$i]; $j <= $linux_sw_ranges[$i + 1]; $j++)
156 $breed_by_swcode[$j] = 'linux';
157
158 // add 'dlink' items into $breed_by_hwcode
159 for ($i = 589; $i <= 637; $i++)
160 $breed_by_hwcode[$i] = 'dlink';
161
162 function detectDeviceBreed ($object_id)
163 {
164 global $breed_by_swcode, $breed_by_hwcode, $breed_by_mgmtcode;
165 foreach (getAttrValues ($object_id) as $record)
166 if ($record['id'] == 4 and array_key_exists ($record['key'], $breed_by_swcode))
167 return $breed_by_swcode[$record['key']];
168 elseif ($record['id'] == 2 and array_key_exists ($record['key'], $breed_by_hwcode))
169 return $breed_by_hwcode[$record['key']];
170 elseif ($record['id'] == 30 and array_key_exists ($record['key'], $breed_by_mgmtcode))
171 return $breed_by_mgmtcode[$record['key']];
172 return '';
173 }
174
175 function assertDeviceBreed ($object_id)
176 {
177 if ('' == $breed = detectDeviceBreed ($object_id))
178 throw new RTGatewayError ('Cannot determine device breed');
179 return $breed;
180 }
181
182 function validBreedFunction ($breed, $command)
183 {
184 global $breedfunc;
185 return array_key_exists ("${breed}-${command}-main", $breedfunc);
186 }
187
188 function assertBreedFunction ($breed, $command)
189 {
190 global $breedfunc;
191 if (! validBreedFunction ($breed, $command))
192 throw new RTGatewayError ("unsupported command '${command}' for breed '${breed}'");
193 return $breedfunc["${breed}-${command}-main"];
194 }
195
196 function queryDevice ($object_id, $command)
197 {
198 $query = translateDeviceCommands ($object_id, array (array ('opcode' => $command)));
199 if ($command == 'xlatepushq')
200 return $query;
201 $breed = assertDeviceBreed ($object_id);
202 $funcname = assertBreedFunction ($breed, $command);
203 require_once 'deviceconfig.php';
204 if (! is_callable ($funcname))
205 throw new RTGatewayError ("undeclared function '${funcname}'");
206
207 for ($i = 0; $i < 3; $i++)
208 try
209 {
210 $ret = $funcname (queryTerminal ($object_id, $query, FALSE));
211 break;
212 }
213 catch (ERetryNeeded $e)
214 {
215 // some devices (e.g. Cisco IOS) refuse to print running configuration
216 // while they are busy. The best way of treating this is retry a few times
217 // before failing the request
218 sleep (3);
219 continue;
220 }
221
222 if (NULL !== ($subst = callHook ('alterDeviceQueryResult', $ret, $object_id, $command)))
223 $ret = $subst;
224 return $ret;
225 }
226
227 function translateDeviceCommands ($object_id, $crq, $vlan_names = NULL)
228 {
229 $breed = assertDeviceBreed ($object_id);
230 $funcname = assertBreedFunction ($breed, 'xlatepushq');
231 require_once 'deviceconfig.php';
232 if (! is_callable ($funcname))
233 throw new RTGatewayError ("undeclared function '${funcname}'");
234 return $funcname ($object_id, $crq, $vlan_names);
235 }
236
237 // takes settings struct (declared in queryTerminal) and CLI commands (plain text) as input by reference
238 // returns an array of command-line parameters to $ref_settings[0]['protocol']
239 // this function is called by callHook, so you can override/chain it
240 // to customize command-line options to particular gateways.
241 function makeGatewayParams ($object_id, $tolerate_remote_errors, /*array(&)*/$ref_settings, /*array(&)*/$ref_commands)
242 {
243 $ret = array();
244 $settings = &$ref_settings[0];
245 $commands = &$ref_commands[0];
246
247 switch ($settings['protocol'])
248 {
249 case 'sshnokey':
250 $params_from_settings['proto'] = 'proto';
251 $params_from_settings['prompt'] = 'prompt';
252 $params_from_settings['prompt-delay'] = 'prompt_delay';
253 $params_from_settings['username'] = 'username';
254 $params_from_settings['password'] = 'password';
255 case 'telnet':
256 case 'netcat':
257 // prepend telnet commands by credentials
258 if (isset ($settings['password']))
259 $commands = $settings['password'] . "\n" . $commands;
260 if (isset ($settings['username']))
261 $commands = $settings['username'] . "\n" . $commands;
262 break;
263 case 'ucssdk': # remote XML through a Python backend
264 # UCS in its current implementation besides the terminal_settings() provides
265 # an additional username/password feed through the HTML from. Whenever the
266 # user provides the credentials through the form, use these instead of the
267 # credentials [supposedly] set by terminal_settings().
268 global $script_mode;
269 if ($script_mode != TRUE && ! isCheckSet ('use_terminal_settings'))
270 {
271 $settings['username'] = assertStringArg ('ucs_login');
272 $settings['password'] = assertStringArg ('ucs_password');
273 }
274 foreach (array ('hostname', 'username', 'password') as $item)
275 if (empty ($settings[$item]))
276 throw new RTGatewayError ("${item} not available, check terminal_settings()");
277 $commands = "login ${settings['hostname']} ${settings['username']} ${settings['password']}\n" . $commands;
278 break;
279 }
280
281 $ret = array();
282 switch ($settings['protocol'])
283 {
284 case 'telnet':
285 $params_from_settings['port'] = 'port';
286 $params_from_settings['prompt'] = 'prompt';
287 $params_from_settings['connect-timeout'] = 'connect_timeout';
288 $params_from_settings['timeout'] = 'timeout';
289 $params_from_settings['prompt-delay'] = 'prompt_delay';
290 $params_from_settings[] = $settings['hostname'];
291 break;
292 case 'netcat':
293 $params_from_settings['p'] = 'port';
294 $params_from_settings['w'] = 'timeout';
295 $params_from_settings['b'] = 'ncbin';
296 $params_from_settings[] = $settings['hostname'];
297 break;
298 case 'ssh':
299 $params_from_settings['sudo-user'] = 'sudo_user';
300 $params_from_settings[] = '--';
301 $params_from_settings['p'] = 'port';
302 $params_from_settings['l'] = 'username';
303 $params_from_settings['i'] = 'identity_file';
304 if (isset ($settings['proto']))
305 switch ($settings['proto'])
306 {
307 case 4:
308 $params_from_settings[] = '-4';
309 break;
310 case 6:
311 $params_from_settings[] = '-6';
312 break;
313 default:
314 throw new RTGatewayError ("Proto '${settings['proto']}' is invalid. Valid protocols are: '4', '6'");
315 }
316 if (isset ($settings['connect_timeout']))
317 $params_from_settings[] = '-oConnectTimeout=' . $settings['connect_timeout'];
318 $params_from_settings[] = '-T';
319 $params_from_settings[] = '-oStrictHostKeyChecking=no';
320 $params_from_settings[] = '-oBatchMode=yes';
321 $params_from_settings[] = '-oCheckHostIP=no';
322 $params_from_settings[] = '-oLogLevel=ERROR';
323 $params_from_settings[] = $settings['hostname'];
324 break;
325 default:
326 throw RTGatewayError ("Invalid terminal protocol '${settings['protocol']}' specified");
327 }
328
329 foreach ($params_from_settings as $param_name => $setting_name)
330 if (is_int ($param_name))
331 $ret[] = $setting_name;
332 elseif (isset ($settings[$setting_name]))
333 $ret[$param_name] = $settings[$setting_name];
334
335 return $ret;
336 }
337
338 // This function returns a text output received from the device
339 // You can override connection settings by implement a callback named 'terminal_settings'.
340 // Errors are thrown as exceptions if not $tolerate_remote_errors, and shown as warnings otherwise.
341 function queryTerminal ($object_id, $commands, $tolerate_remote_errors = TRUE)
342 {
343 $objectInfo = spotEntity ('object', $object_id);
344 $endpoints = findAllEndpoints ($object_id, $objectInfo['name']);
345 if (count ($endpoints) == 0)
346 throw new RTGatewayError ('no management address set');
347 if (count ($endpoints) > 1)
348 throw new RTGatewayError ('cannot pick management address');
349
350 // telnet prompt and mode specification
351 switch ($breed = detectDeviceBreed ($object_id))
352 {
353 case 'ios12':
354 case 'ftos8':
355 $protocol = 'netcat'; // default is netcat mode
356 $prompt = '^(Login|Username|Password): $|^\S+[>#]$|\[[^][]*\]\? $'; // set the prompt in case user would like to specify telnet protocol
357 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
358 break;
359 case 'air12':
360 $protocol = 'telnet'; # Aironet IOS is broken
361 $prompt = '^(Username|Password): $|^\S+[>#]$';
362 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
363 break;
364 case 'fdry5':
365 $protocol = 'netcat'; // default is netcat mode
366 $prompt = '^(Login|Username|Password): $|^\S+[>#]$'; // set the prompt in case user would like to specify telnet protocol
367 $commands = "skip-page-display\n" . $commands;
368 break;
369 case 'vrp55':
370 $commands = "screen-length 0 temporary\n" . $commands;
371 /* fall-through */
372 case 'vrp53':
373 $protocol = 'telnet';
374 $prompt = '^\[[^[\]]+\]$|^<[^<>]+>$|^(Username|Password):$|(?:\[Y\/N\]|\(Y\/N\)\[[YN]\]):?$';
375 break;
376 case 'nxos4':
377 $protocol = 'telnet';
378 $prompt = '(^([Ll]ogin|[Pp]assword):|[>#]) $';
379 $commands = "terminal length 0\nterminal no monitor\n" . $commands;
380 break;
381 case 'xos12':
382 $protocol = 'telnet';
383 $prompt = ': $|\.\d+ # $|\?\s*\([Yy]\/[Nn]\)\s*$';
384 $commands = "disable clipaging\n" . $commands;
385 break;
386 case 'jun10':
387 $protocol = 'telnet';
388 $prompt = '^login: $|^Password:$|^\S+@\S+[>#] $';
389 $commands = "set cli screen-length 0\n" . $commands;
390 break;
391 case 'eos4':
392 $protocol = 'telnet'; # strict RFC854 implementation, netcat won't work
393 $prompt = '^\xf2?(login|Username|Password): $|^\S+[>#]$';
394 $commands = "enable\nno terminal monitor\nterminal length 0\n" . $commands;
395 break;
396 case 'ros11':
397 $protocol = 'netcat'; # see ftos8 case
398 $prompt = '^(User Name|\rPassword):$|^\r?\S+# $';
399 $commands = "terminal datadump\n" . $commands;
400 $commands .= "\n\n"; # temporary workaround for telnet server
401 break;
402 case 'iosxr4':
403 $protocol = 'telnet';
404 $prompt = '^\r?(Login|Username|Password): $|^\r?\S+[>#]$';
405 $commands = "terminal length 0\nterminal monitor disable\n" . $commands;
406 break;
407 case 'ucs':
408 $protocol = 'ucssdk';
409 break;
410 case 'dlink':
411 $protocol = 'netcat';
412 $commands = "disable clipaging\n" . $commands;
413 break;
414 }
415 if (! isset ($protocol))
416 $protocol = 'netcat';
417 if (! isset ($prompt))
418 $prompt = NULL;
419
420 // set the default settings before calling user-defined callback
421 $settings = array
422 (
423 'hostname' => $endpoints[0],
424 'protocol' => $protocol,
425 'port' => NULL,
426 'prompt' => $prompt,
427 'username' => NULL,
428 'password' => NULL,
429 'timeout' => 15,
430 'connect_timeout' => 2,
431 'prompt_delay' => 0.001, # 1ms
432 'sudo_user' => NULL,
433 'identity_file' => NULL,
434 );
435
436 // override default settings
437 if (is_callable ('terminal_settings'))
438 call_user_func ('terminal_settings', $objectInfo, array (&$settings));
439 // make gateway-specific CLI params out of settings
440 $params = callHook ('makeGatewayParams', $object_id, $tolerate_remote_errors, array (&$settings), array (&$commands));
441 // call gateway
442 $ret_code = callScript ($settings['protocol'], $params, $commands, $out, $errors);
443
444 if ($settings['protocol'] != 'ssh' || ! $tolerate_remote_errors)
445 {
446 if (! empty ($errors))
447 throw new RTGatewayError ("${settings['protocol']} error: " . rtrim ($errors));
448 elseif ($ret_code !== 0)
449 throw new RTGatewayError ("${settings['protocol']} error: result code $ret_code");
450 }
451 elseif (! empty ($errors)) // ssh and tolerate and non-empty $errors
452 foreach (explode ("\n", $errors) as $line)
453 if (strlen ($line))
454 showWarning ("${settings['protocol']} ${settings['hostname']}: $line");
455 return strtr($out, array("\r" => "")); // cut ^M symbols
456 }
457
458 function callScript ($gwname, $params, $in, &$out, &$errors)
459 {
460 global $racktables_gwdir, $local_gwdir, $gateway_log;
461 if (isset ($gateway_log))
462 $gateway_log = '';
463
464 $cwd = NULL;
465 if ('/' === substr ($gwname, 0, 1))
466 // absolute path to executable
467 $binary = $gwname;
468 else
469 {
470 // path relative to one of RackTables' gwdirs
471 if (isset ($local_gwdir) && file_exists ("$local_gwdir/$gwname"))
472 $cwd = $local_gwdir;
473 elseif (isset ($racktables_gwdir) && file_exists ("$racktables_gwdir/$gwname"))
474 $cwd = $racktables_gwdir;
475 if (! isset ($cwd))
476 throw new RTGatewayError ("Could not find the gateway file called '$gwname'");
477 $binary = "./$gwname";
478 }
479
480 $cmd_line = $binary;
481 foreach ($params as $key => $value)
482 {
483 if (! isset ($value))
484 continue;
485 if (preg_match ('/^\d+$/', $key))
486 $cmd_line .= " " . escapeshellarg ($value);
487 else
488 {
489 if (strlen ($key) == 1)
490 $cmd_line .= " " . escapeshellarg ("-$key") . " " . escapeshellarg ($value);
491 else
492 $cmd_line .= " " . escapeshellarg("--$key=$value");
493 }
494 }
495
496 $pipes = array();
497 $child = proc_open
498 (
499 $cmd_line,
500 array (
501 0 => array ('pipe', 'r'),
502 1 => array ('pipe', 'w'),
503 2 => array ('pipe', 'w'),
504 ),
505 $pipes,
506 $cwd
507 );
508 if (! is_resource ($child))
509 throw new RTGatewayError ("cant execute $binary");
510
511 $buff_size = 4096;
512 $write_left = array ($pipes[0]);
513 $read_left = array ($pipes[1], $pipes[2]);
514 $write_fd = $write_left;
515 $read_fd = $read_left;
516 $except_fd = array();
517 $out = '';
518 $errors = '';
519 while ((! empty ($read_fd) || ! empty ($write_fd)) && stream_select ($read_fd, $write_fd, $except_fd, NULL))
520 {
521 foreach ($write_fd as $fd)
522 {
523 $written = fwrite ($fd, $in, $buff_size);
524 // log all communication data into global var
525 if ($written != 0 && isset ($gateway_log))
526 $gateway_log .= preg_replace ('/^/m', '> ', substr ($in, 0, $written));
527 $in = substr ($in, $written);
528
529 if ($written == 0 || empty ($in))
530 {
531 // close input fd
532 $write_left = array_diff ($write_left, array ($fd));
533 fclose ($fd);
534 }
535 }
536 foreach ($read_fd as $fd)
537 {
538 $str = fread ($fd, $buff_size);
539 if (strlen ($str) == 0)
540 {
541 // close output fd
542 $read_left = array_diff ($read_left, array ($fd));
543 fclose ($fd);
544 }
545 else
546 {
547 // log all communication data into global var
548 if (isset ($gateway_log))
549 $gateway_log .= $str;
550
551 if ($fd == $pipes[1])
552 $out .= $str;
553 elseif ($fd == $pipes[2])
554 $errors .= $str;
555 }
556 }
557
558 $write_fd = $write_left;
559 $read_fd = $read_left;
560
561 // limit the size of gateway_log
562 if (isset ($gateway_log) && strlen ($gateway_log) > MAX_GW_LOGSIZE * 1.1)
563 $gateway_log = substr ($gateway_log, -MAX_GW_LOGSIZE);
564
565 }
566 return proc_close ($child);
567 }
568
569 function getRunning8021QConfig ($object_id)
570 {
571 $ret = queryDevice ($object_id, 'get8021q');
572 // Once there is no default VLAN in the parsed data, it means
573 // something else was parsed instead of config text.
574 if (!in_array (VLAN_DFL_ID, $ret['vlanlist']) || empty ($ret['portdata']))
575 throw new RTGatewayError ('communication with device failed');
576 return $ret;
577 }
578
579 function setDevice8021QConfig ($object_id, $pseudocode, $vlan_names)
580 {
581 // FIXME: this is a perfect place to log intended changes
582 // $object_id argument isn't used by default translating functions, but
583 // may come in handy for overloaded versions of these.
584 $commands = translateDeviceCommands ($object_id, $pseudocode, $vlan_names);
585 $breed = detectDeviceBreed ($object_id);
586 $output = queryTerminal ($object_id, $commands, FALSE);
587
588 // throw an exception if Juniper did not allow to enter config mode or to commit changes
589 if ($breed == 'jun10')
590 {
591 if (preg_match ('/>\s*configure exclusive\s*$[^#>]*?^error:/sm', $output))
592 throw new RTGatewayError ("Configuration is locked by other user");
593 elseif (preg_match ('/#\s*commit\s*$([^#]*?^error: .*?)$/sm', $output, $m))
594 throw new RTGatewayError ("Commit failed: ${m[1]}");
595 }
596 }
597
598 ?>