source: web/old/remctl-2.14/php/php_remctl.c @ f6f3e91

web
Last change on this file since f6f3e91 was f6f3e91, checked in by Jessica B. Hamrick <jhamrick@…>, 15 years ago

Preserve directory hierarchy (not sure what happened to it)

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * remctl PECL extension for PHP
3 *
4 * Provides bindings for PHP very similar to the libremctl library for C or
5 * the Net::Remctl bindings for Perl.
6 *
7 * Originally written by Andrew Mortensen <admorten@umich.edu>, 2008
8 * Copyright 2008 Andrew Mortensen <admorten@umich.edu>
9 * Copyright 2008 Board of Trustees, Leland Stanford Jr. University
10 *
11 * See LICENSE for licensing terms.
12 */
13
14#include <config.h>
15
16#include <errno.h>
17#include <fcntl.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22
23#include <client/remctl.h>
24
25#include <php.h>
26#include <php_remctl.h>
27
28static int le_remctl_internal;
29
30static zend_function_entry remctl_functions[] = {
31    ZEND_FE(remctl,         NULL)
32    ZEND_FE(remctl_new,     NULL)
33    ZEND_FE(remctl_open,    NULL)
34    ZEND_FE(remctl_close,   NULL)
35    ZEND_FE(remctl_command, NULL)
36    ZEND_FE(remctl_output,  NULL)
37    ZEND_FE(remctl_error,   NULL)
38    { NULL, NULL, NULL, 0, 0 }
39};
40
41zend_module_entry remctl_module_entry = {
42    STANDARD_MODULE_HEADER,
43    PHP_REMCTL_EXTNAME,
44    remctl_functions,
45    PHP_MINIT(remctl),
46    NULL,
47    NULL,
48    NULL,
49    NULL,
50    PHP_REMCTL_VERSION,
51    STANDARD_MODULE_PROPERTIES
52};
53
54#ifdef COMPILE_DL_REMCTL
55ZEND_GET_MODULE(remctl)
56#endif
57
58/*
59 * Destructor for a remctl object.  Close the underlying connection.
60 */
61static void
62php_remctl_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
63{
64    struct remctl *r = (struct remctl *) rsrc->ptr;
65
66    if (r != NULL)
67        remctl_close(r);
68}
69
70
71/*
72 * Initialize the module and register the destructor.  Stores the resource
73 * number of the module in le_remctl_internal.
74 */
75PHP_MINIT_FUNCTION(remctl)
76{
77    le_remctl_internal =
78        zend_register_list_destructors_ex(php_remctl_dtor, NULL,
79            PHP_REMCTL_RES_NAME, module_number);
80    return SUCCESS;
81}
82
83
84/*
85 * The simplified interface.  Make a call and return the results as an
86 * object.
87 */
88ZEND_FUNCTION(remctl)
89{
90    zval *cmd_array, **data;
91    void **data_ref;
92    HashTable *hash;
93    HashPosition pos;
94    char *host, *principal = NULL;
95    const char **command = NULL;
96    long port;
97    int hlen, plen, count, i, status;
98    int success = 0;
99    struct remctl_result *result = NULL;
100
101    /*
102     * Read the arguments (host, port, principal, and command) and check their
103     * validity.  Host and command are required, so all arguments must be
104     * provided, but an empty string can be passed in as the principal.
105     */
106    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slsa", &host,
107                 &hlen, &port, &principal, &plen, &cmd_array);
108    if (status == FAILURE) {
109        zend_error(E_WARNING, "remctl: invalid parameters\n");
110        RETURN_NULL();
111    }
112    if (hlen == 0) {
113        zend_error(E_WARNING, "remctl: host must be a valid string\n");
114        RETURN_NULL();
115    }
116    if (plen == 0)
117        principal = NULL;
118    hash = Z_ARRVAL_P(cmd_array);
119    count = zend_hash_num_elements(hash);
120    if (count < 1) {
121        zend_error(E_WARNING, "remctl: command must not be empty\n");
122        RETURN_NULL();
123    }
124
125    /*
126     * Convert the command array into the char ** needed by libremctl.  This
127     * is less than ideal because we make another copy of all of the
128     * arguments.  There should be some way to do this without making a copy.
129     */
130    command = emalloc((count + 1) * sizeof(char *));
131    if (command == NULL) {
132        zend_error(E_WARNING, "remctl: emalloc failed\n");
133        RETURN_NULL();
134    }
135    i = 0;
136    zend_hash_internal_pointer_reset_ex(hash, &pos);
137    data_ref = (void **) &data;
138    while (zend_hash_get_current_data_ex(hash, data_ref, &pos) == SUCCESS) {
139        if (Z_TYPE_PP(data) != IS_STRING) {
140            zend_error(E_WARNING, "remctl: command contains non-string\n");
141            goto cleanup;
142        }
143        if (i >= count) {
144            zend_error(E_WARNING, "remctl: internal error: incorrect count\n");
145            goto cleanup;
146        }
147        command[i] = estrndup(Z_STRVAL_PP(data), Z_STRLEN_PP(data));
148        if (command[i] == NULL) {
149            zend_error(E_WARNING, "remctl: estrndup failed\n");
150            count = i;
151            goto cleanup;
152        }
153        i++;
154        zend_hash_move_forward_ex(hash, &pos);
155    }
156    command[count] = NULL;
157
158    /* Run the actual remctl call. */
159    result = remctl(host, port, principal, command);
160    if (result == NULL) {
161        zend_error(E_WARNING, "remctl: %s\n", strerror(errno));
162        goto cleanup;
163    }
164
165    /*
166     * Convert the remctl result to an object.  return_value is defined for us
167     * by Zend.
168     */
169    if (object_init(return_value) != SUCCESS) {
170        zend_error(E_WARNING, "remctl: object_init failed\n");
171        goto cleanup;
172    }
173    if (result->error == NULL)
174        add_property_string(return_value, "error", "", 1);
175    else
176        add_property_string(return_value, "error", result->error, 1);
177    add_property_stringl(return_value, "stdout", result->stdout_buf,
178        result->stdout_len, 1);
179    add_property_long(return_value, "stdout_len", result->stdout_len);
180    add_property_stringl(return_value, "stderr", result->stderr_buf,
181        result->stderr_len, 1);
182    add_property_long(return_value, "stderr_len", result->stderr_len);
183    add_property_long(return_value, "status", result->status);
184    success = 1;
185
186cleanup:
187    if (command != NULL) {
188        for (i = 0; i < count; i++)
189            efree((char *) command[i]);
190        efree(command);
191    }
192    if (result != NULL)
193        remctl_result_free(result);
194    if (!success)
195        RETURN_NULL();
196}
197
198
199/*
200 * Now the full interface.  First, the constructor.
201 */
202ZEND_FUNCTION(remctl_new)
203{
204    struct remctl *r;
205
206    r = remctl_new();
207    if (r == NULL) {
208        zend_error(E_WARNING, "remctl_new: %s", strerror(errno));
209        RETURN_NULL();
210    }
211    ZEND_REGISTER_RESOURCE(return_value, r, le_remctl_internal);
212}
213
214
215/*
216 * Open a connection to the remote host.  Only the host parameter is required;
217 * the rest are optional.  PHP may require something be passed in for
218 * principal, but the empty string is taken to mean "use the library default."
219 */
220ZEND_FUNCTION(remctl_open)
221{
222    struct remctl *r;
223    zval *zrem;
224    char *host;
225    char *principal = NULL;
226    long port = 0;
227    int hlen, plen, status;
228
229    /* Parse and verify arguments. */
230    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|ls", &zrem,
231                 &host, &hlen, &port, &principal, &plen);
232    if (status == FAILURE) {
233        zend_error(E_WARNING, "remctl_open: invalid parameters\n");
234        RETURN_FALSE;
235    }
236    if (plen == 0)
237        principal = NULL;
238    ZEND_FETCH_RESOURCE(r, struct remctl *, &zrem, -1, PHP_REMCTL_RES_NAME,
239        le_remctl_internal);
240
241    /* Now we have all the arguments and can do the real work. */
242    if (!remctl_open(r, host, port, principal))
243        RETURN_FALSE;
244    RETURN_TRUE;
245}
246
247
248/*
249 * Send a command to the remote server.
250 */
251ZEND_FUNCTION(remctl_command)
252{
253    struct remctl *r;
254    zval *zrem, *cmd_array, **data;
255    void **data_ref;
256    HashTable *hash;
257    HashPosition pos;
258    struct iovec *cmd_vec = NULL;
259    int i, count, status;
260    int success = 0;
261
262    /* Parse and verify arguments. */
263    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &zrem,
264                 &cmd_array);
265    if (status == FAILURE) {
266        zend_error(E_WARNING, "remctl_command: invalid parameters\n");
267        RETURN_FALSE;
268    }
269    ZEND_FETCH_RESOURCE(r, struct remctl *, &zrem, -1, PHP_REMCTL_RES_NAME,
270        le_remctl_internal);
271    hash = Z_ARRVAL_P(cmd_array);
272    count = zend_hash_num_elements(hash);
273    if (count < 1) {
274        zend_error(E_WARNING, "remctl_command: command must not be empty\n");
275        RETURN_NULL();
276    }
277
278    /*
279     * Transform the PHP array into an array of struct iovec.  This is less
280     * than ideal because it makes another copy of all of the data.  There
281     * should be some way to do this without copying.
282     */
283    cmd_vec = emalloc(count * sizeof(struct iovec));
284    if (cmd_vec == NULL) {
285        zend_error(E_WARNING, "remctl_command: emalloc failed\n");
286        RETURN_FALSE;
287    }
288    i = 0;
289    zend_hash_internal_pointer_reset_ex(hash, &pos);
290    data_ref = (void **) &data;
291    while (zend_hash_get_current_data_ex(hash, data_ref, &pos) == SUCCESS) {
292        if (Z_TYPE_PP(data) != IS_STRING) {
293            zend_error(E_WARNING,
294                "remctl_command: command contains non-string\n");
295            goto cleanup;
296        }
297        if (i >= count) {
298            zend_error(E_WARNING,
299                "remctl_command: internal error: incorrect count\n");
300            goto cleanup;
301        }
302        cmd_vec[i].iov_base = emalloc(Z_STRLEN_PP(data) + 1);
303        if (cmd_vec[i].iov_base == NULL) {
304            zend_error(E_WARNING, "remctl_command: emalloc failed\n");
305            count = i;
306            goto cleanup;
307        }
308        cmd_vec[i].iov_len = Z_STRLEN_PP(data);
309        memcpy(cmd_vec[i].iov_base, Z_STRVAL_PP(data), cmd_vec[i].iov_len);
310        i++;
311        zend_hash_move_forward_ex(hash, &pos);
312    }
313
314    /* Finally, we can do the work. */
315    if (!remctl_commandv(r, cmd_vec, count))
316        goto cleanup;
317    success = 1;
318
319cleanup:
320    if (cmd_vec != NULL) {
321        for (i = 0; i < count; i++)
322            efree(cmd_vec[i].iov_base);
323        efree(cmd_vec);
324    }
325    if (!success)
326        RETURN_FALSE;
327    RETURN_TRUE;
328}
329
330
331/*
332 * Get an output token from the server and return it as an object.
333 */
334ZEND_FUNCTION(remctl_output)
335{
336    struct remctl *r;
337    struct remctl_output *output;
338    zval *zrem;
339    int status;
340
341    /* Parse and verify arguments. */
342    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zrem);
343    if (status == FAILURE) {
344        zend_error(E_WARNING, "remctl_output: invalid parameters\n");
345        RETURN_NULL();
346    }
347    ZEND_FETCH_RESOURCE(r, struct remctl *, &zrem, -1, PHP_REMCTL_RES_NAME,
348        le_remctl_internal);
349
350    /* Get the output token. */
351    output = remctl_output(r);
352    if (output == NULL) {
353        zend_error(E_WARNING, "remctl_output: error reading from server: %s",
354            remctl_error(r));
355        RETURN_NULL();
356    }
357
358    /*
359     * Populate an object with the output results.  return_value is defined
360     * for us by Zend.
361     */
362    if (object_init(return_value) != SUCCESS) {
363        zend_error(E_WARNING, "remctl_output: object_init failed\n");
364        RETURN_NULL();
365    }
366    switch (output->type) {
367    case REMCTL_OUT_OUTPUT:
368        add_property_string(return_value, "type", "output", 1);
369        add_property_stringl(return_value, "data", output->data,
370            output->length, 1);
371        add_property_long(return_value, "stream", output->stream);
372        break;
373    case REMCTL_OUT_ERROR:
374        add_property_string(return_value, "type", "error", 1);
375        add_property_stringl(return_value, "data", output->data,
376            output->length, 1);
377        add_property_long(return_value, "error", output->error);
378        break;
379    case REMCTL_OUT_STATUS:
380        add_property_string(return_value, "type", "status", 1);
381        add_property_long(return_value, "status", output->status);
382        break;
383    case REMCTL_OUT_DONE:
384        add_property_string(return_value, "type", "done", 1);
385        break;
386    }
387}
388
389
390/*
391 * Returns the error message from a previously failed remctl call.
392 */
393ZEND_FUNCTION(remctl_error)
394{
395    struct remctl *r;
396    zval *zrem;
397    const char *error;
398    int status;
399
400    /* Parse and verify arguments. */
401    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zrem);
402    if (status == FAILURE) {
403        zend_error(E_WARNING, "remctl_error: invalid parameters\n");
404        RETURN_NULL();
405    }
406    ZEND_FETCH_RESOURCE(r, struct remctl *, &zrem, -1, PHP_REMCTL_RES_NAME,
407        le_remctl_internal);
408
409    /* Do the work. */
410    error = remctl_error(r);
411    RETURN_STRING((char *) error, 1);
412}
413
414
415/*
416 * Close the connection.  This isn't strictly necessary since the destructor
417 * will close the connection for us, but it's part of the interface.
418 */
419ZEND_FUNCTION(remctl_close)
420{
421    struct remctl *r;
422    zval *zrem;
423    int status;
424
425    /* Parse and verify arguments. */
426    status = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zrem);
427    if (status == FAILURE) {
428        zend_error(E_WARNING, "remctl_error: invalid parameters\n");
429        RETURN_NULL();
430    }
431    ZEND_FETCH_RESOURCE(r, struct remctl *, &zrem, -1, PHP_REMCTL_RES_NAME,
432        le_remctl_internal);
433
434    /* This delete invokes php_remctl_dtor, which calls remctl_close. */
435    zend_list_delete(Z_LVAL_P(zrem));
436    RETURN_TRUE;
437}
Note: See TracBrowser for help on using the repository browser.