| 1 | remctl PECL Extension for PHP |
|---|
| 2 | |
|---|
| 3 | OVERVIEW |
|---|
| 4 | |
|---|
| 5 | The remctl PECL extension for PHP provides PHP bindings to the libremctl |
|---|
| 6 | client library. The provided interface is roughly the same as the C and |
|---|
| 7 | Perl interfaces, with some minor variations to be more consistent with |
|---|
| 8 | the normal PHP function interface. |
|---|
| 9 | |
|---|
| 10 | This PECL extension provides two interfaces, one which performs a single |
|---|
| 11 | call to a remctl server and returns the result, and another which |
|---|
| 12 | provides more control over the connection, returns individual output |
|---|
| 13 | tokens, and allows multiple commands to be sent via the same connection. |
|---|
| 14 | |
|---|
| 15 | REQUIREMENTS |
|---|
| 16 | |
|---|
| 17 | The module has only been tested with PHP 5.2 and may or may not work |
|---|
| 18 | with earlier versions. As with all libremctl bindings, it does not |
|---|
| 19 | itself obtain Kerberos tickets and requires that a Kerberos ticket cache |
|---|
| 20 | already be set up before making remctl calls. |
|---|
| 21 | |
|---|
| 22 | The PECL module build infrastructure is created on the fly by the |
|---|
| 23 | configure script for the main remctl package using phpize. This means |
|---|
| 24 | that you must have phpize and all of its dependencies installed to build |
|---|
| 25 | the PECL module. |
|---|
| 26 | |
|---|
| 27 | SIMPLIFIED INTERFACE |
|---|
| 28 | |
|---|
| 29 | remctl(HOSTNAME, PORT, PRINCIPAL, COMMAND) |
|---|
| 30 | Runs COMMAND on the remote system and returns an object containing |
|---|
| 31 | the results. COMMAND should be an array of the command, the |
|---|
| 32 | subcommand, and any parameters. HOSTNAME is the remote host to |
|---|
| 33 | connect to. PORT is the port; pass 0 to use the default library |
|---|
| 34 | behavior (first try 4373 and then fall back to 4444). PRINCIPAL is |
|---|
| 35 | the principal of the server to use for authentication; pass in the |
|---|
| 36 | empty string to use the default of host/HOSTNAME with the realm |
|---|
| 37 | determined by domain-realm mapping. |
|---|
| 38 | |
|---|
| 39 | The return value of remctl() is an object which will have the following |
|---|
| 40 | properties: |
|---|
| 41 | |
|---|
| 42 | error |
|---|
| 43 | The error message from the remote host or the local client library |
|---|
| 44 | if the remctl command fails. Set to the empty string if there was |
|---|
| 45 | no error. Checking whether error is the empty string is the |
|---|
| 46 | suppported way of determining whether the call succeeded. |
|---|
| 47 | |
|---|
| 48 | stdout |
|---|
| 49 | The command's standard output or null if there was none. |
|---|
| 50 | |
|---|
| 51 | stdout_len |
|---|
| 52 | The length of the command's standard output or 0 if there was none. |
|---|
| 53 | |
|---|
| 54 | stderr |
|---|
| 55 | The command's standard error or null if there was none. |
|---|
| 56 | |
|---|
| 57 | stderr_len |
|---|
| 58 | The length of the command's standard error or 0 if there was none. |
|---|
| 59 | |
|---|
| 60 | status |
|---|
| 61 | The exit status of the command. |
|---|
| 62 | |
|---|
| 63 | Here is an example using the simplified interface: |
|---|
| 64 | |
|---|
| 65 | dl('remctl.so'); |
|---|
| 66 | if (!extension_loaded('remctl')) { |
|---|
| 67 | echo "Failed to load remctl extension\n"; |
|---|
| 68 | exit(2); |
|---|
| 69 | } |
|---|
| 70 | $command = array('test', 'echo', 'hello world'); |
|---|
| 71 | $result = remctl('server.example.com', 0, '', $command); |
|---|
| 72 | if ($result->error) { |
|---|
| 73 | echo "remctl failed: $result->error\n"; |
|---|
| 74 | exit(2); |
|---|
| 75 | } |
|---|
| 76 | if ($result->stdout_len) { |
|---|
| 77 | echo "stdout: $result->stdout"; |
|---|
| 78 | } |
|---|
| 79 | if ($result->stderr_len) { |
|---|
| 80 | echo "stderr: $result->stderr"; |
|---|
| 81 | } |
|---|
| 82 | echo "status: $result->status"; |
|---|
| 83 | |
|---|
| 84 | Each call to remctl() will open a new connection to the remote host and |
|---|
| 85 | close it after retrieving the results of the command. |
|---|
| 86 | |
|---|
| 87 | FULL INTERFACE |
|---|
| 88 | |
|---|
| 89 | The full remctl interface requires the user to do more bookkeeping, but |
|---|
| 90 | provides more flexibility and visibility into what is happening at a |
|---|
| 91 | protocol level. It allows issuing multiple commands on the same |
|---|
| 92 | persistant connection (provided that the remote server supports protocol |
|---|
| 93 | version two; if it doesn't, the library will transparently fall back to |
|---|
| 94 | opening a connection for each command). |
|---|
| 95 | |
|---|
| 96 | To use the full interface, first create a connection object with |
|---|
| 97 | remctl_new(), connect to a server with remctl_open(), and then |
|---|
| 98 | issue a command with remctl_command() and read output tokens with |
|---|
| 99 | remctl_output(). Once a status token has been received, the command is |
|---|
| 100 | complete and another command can be issued. |
|---|
| 101 | |
|---|
| 102 | The provided functions are: |
|---|
| 103 | |
|---|
| 104 | remctl_new() |
|---|
| 105 | Create a new connection object. This doesn't attempt to connect to |
|---|
| 106 | a host and will only fail if the extension cannot allocate memory. |
|---|
| 107 | |
|---|
| 108 | remctl_error(CONNECTION) |
|---|
| 109 | Returns, as a string, the error message from the last failed |
|---|
| 110 | operation on the connection object CONNECTION. |
|---|
| 111 | |
|---|
| 112 | remctl_open(CONNECTION, HOSTNAME[, PORT[, PRINCIPAL]]) |
|---|
| 113 | Connect to HOSTNAME on port PORT using PRINCIPAL as the remote |
|---|
| 114 | server's principal for authentication. If PORT is omitted or 0, use |
|---|
| 115 | the default (first try 4373, the registered remctl port, and fall |
|---|
| 116 | back to the legacy 4444 port if that fails). If PRINCIPAL is |
|---|
| 117 | omitted or the empty string, use the default of host/HOSTNAME, with |
|---|
| 118 | the realm determined by domain-realm mapping. Returns true on |
|---|
| 119 | success, false on failure. |
|---|
| 120 | |
|---|
| 121 | remctl_command(CONNECTION, COMMAND) |
|---|
| 122 | Send COMMAND (which should be an array) to the remote host. The |
|---|
| 123 | command may, under the remctl protocol, contain any character, but |
|---|
| 124 | be aware that most remctl servers will reject commands or arguments |
|---|
| 125 | containing ASCII 0 (NUL). This currently therefore cannot be used |
|---|
| 126 | for upload of arbitrary unencoded binary data. Returns true on |
|---|
| 127 | success (meaning success in sending the command and implying nothing |
|---|
| 128 | about the result of the command), and false on failure. |
|---|
| 129 | |
|---|
| 130 | remctl_output(CONNECTION) |
|---|
| 131 | Returns the next output token from the remote host. This will be an |
|---|
| 132 | object with one or more of the following properties: |
|---|
| 133 | |
|---|
| 134 | type |
|---|
| 135 | The type of the output token, which will be one of "output", |
|---|
| 136 | "error", "status", or "done". A command will result in either |
|---|
| 137 | one "error" token or zero or more "output" tokens followed by a |
|---|
| 138 | "status" token. The output is complete as soon as any token |
|---|
| 139 | other than an "output" token has been received, but the library |
|---|
| 140 | will keep returning "done" tokens to the caller for as long as |
|---|
| 141 | remctl_output() is called without another remctl_command(). |
|---|
| 142 | |
|---|
| 143 | data |
|---|
| 144 | Returns the contents of the token for either an "error" or |
|---|
| 145 | "output" token. The returned data may contain any character, |
|---|
| 146 | including ASCII 0 (NUL). |
|---|
| 147 | |
|---|
| 148 | stream |
|---|
| 149 | For an "output" token, returns the stream with which the data is |
|---|
| 150 | associated. Currently, this will either be 1 for standard |
|---|
| 151 | output or 2 for standard error. This value is undefined for all |
|---|
| 152 | other token types. |
|---|
| 153 | |
|---|
| 154 | status |
|---|
| 155 | For a "status" token, returns the exit status of the remote |
|---|
| 156 | command. This value is undefined for all other token types. |
|---|
| 157 | |
|---|
| 158 | error |
|---|
| 159 | For an "error" token, returns the remctl error code for the |
|---|
| 160 | protocol error. The text message will be returned in data. |
|---|
| 161 | |
|---|
| 162 | remctl_close(CONNECTION) |
|---|
| 163 | Explicitly close the connection and destroy the connection object. |
|---|
| 164 | This will also be done automatically when the object is destroyed, |
|---|
| 165 | so calling remctl_close explicitly is often not necessary. |
|---|
| 166 | |
|---|
| 167 | Here is an example using the full interface: |
|---|
| 168 | |
|---|
| 169 | dl('remctl.so'); |
|---|
| 170 | if (!extension_loaded('remctl')) { |
|---|
| 171 | echo "Failed to load remctl extension\n"; |
|---|
| 172 | exit(2); |
|---|
| 173 | } |
|---|
| 174 | $r = remctl_new(); |
|---|
| 175 | if ($r == null) { |
|---|
| 176 | echo "remctl_new failed\n"; |
|---|
| 177 | exit(2); |
|---|
| 178 | } |
|---|
| 179 | if (!remctl_open($r, 'server.example.com')) { |
|---|
| 180 | echo "remctl_open failed: " . remctl_error($r) . "\n"; |
|---|
| 181 | exit(2); |
|---|
| 182 | } |
|---|
| 183 | $command = array('test', 'echo', 'hello world'); |
|---|
| 184 | if (!remctl_command($r, $command)) { |
|---|
| 185 | echo "remctl_command failed: " . remctl_error($r) . "\n"; |
|---|
| 186 | } |
|---|
| 187 | $output = remctl_output($r); |
|---|
| 188 | while ($output != null && $output->type != "done") { |
|---|
| 189 | switch($output->type) { |
|---|
| 190 | case "output": |
|---|
| 191 | if ($output->stream == 1) { |
|---|
| 192 | echo "stdout: $output->data"; |
|---|
| 193 | } elseif ($output->stream == 2) { |
|---|
| 194 | echo "stderr: $output->data"; |
|---|
| 195 | } |
|---|
| 196 | break; |
|---|
| 197 | case "error": |
|---|
| 198 | echo "error: $output->error ($output->data)\n"; |
|---|
| 199 | break; |
|---|
| 200 | case "status": |
|---|
| 201 | echo "status: $output->status\n"; |
|---|
| 202 | break; |
|---|
| 203 | default: |
|---|
| 204 | echo "unknown output token type $output->type\n"; |
|---|
| 205 | } |
|---|
| 206 | $output = remctl_output($r); |
|---|
| 207 | } |
|---|
| 208 | if ($output == null) { |
|---|
| 209 | echo "remctl_output failed: " . remctl_error($r) . "\n"; |
|---|
| 210 | exit(2); |
|---|
| 211 | } |
|---|
| 212 | remctl_close($r) |
|---|
| 213 | |
|---|
| 214 | As mentioned above, the final remctl_close() is normally not needed. |
|---|
| 215 | |
|---|
| 216 | HISTORY |
|---|
| 217 | |
|---|
| 218 | This binding was originally written by Andrew Mortensen. part of the |
|---|
| 219 | stock remctl distribution and ongoing maintenance is done by Russ |
|---|
| 220 | Allbery. |
|---|