source: web/old/remctl-2.14/client/api.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: 10.8 KB
Line 
1/*
2 * Entry points for the remctl library API.
3 *
4 * All of the high-level entry points for the remctl library API, defined in
5 * remctl.h, are found here.  The detailed implementation of these APIs are
6 * in other source files.
7 *
8 * All public functions defined here should use int to return boolean values.
9 * We don't try to set up the portability glue to use bool in our public
10 * headers.
11 *
12 * Written by Russ Allbery <rra@stanford.edu>
13 * Based on work by Anton Ushakov
14 * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008
15 *     Board of Trustees, Leland Stanford Jr. University
16 *
17 * See LICENSE for licensing terms.
18 */
19
20#include <config.h>
21#include <portable/system.h>
22#include <portable/gssapi.h>
23#include <portable/socket.h>
24#include <portable/uio.h>
25
26#include <errno.h>
27
28#include <client/internal.h>
29#include <client/remctl.h>
30#include <util/util.h>
31
32
33/*
34 * Handle an internal failure for the simplified interface.  We try to grab
35 * the remctl error and put it into the error field in the remctl result, but
36 * if that fails, we free the remctl result and return NULL to indicate a
37 * fatal error.
38 */
39static struct remctl_result *
40internal_fail(struct remctl *r, struct remctl_result *result)
41{
42    const char *error;
43
44    error = remctl_error(r);
45    if (error == NULL) {
46        remctl_close(r);
47        remctl_result_free(result);
48        return NULL;
49    } else {
50        result->error = strdup(error);
51        remctl_close(r);
52        if (result->error != NULL)
53            return result;
54        else {
55            remctl_result_free(result);
56            return NULL;
57        }
58    }
59}
60
61
62/*
63 * Given a struct remctl_result into which we're accumulating output and a
64 * struct remctl_output that contains a fragment of output, append the output
65 * to the appropriate slot in the result.  This is not particularly efficient.
66 * Returns false if something fails and tries to set result->error; if we
67 * can't even do that, make sure it's set to NULL.
68 */
69static bool
70internal_output_append(struct remctl_result *result,
71                       struct remctl_output *output)
72{
73    char **buffer = NULL;
74    size_t *length = NULL;
75    char *old, *newbuf;
76    size_t oldlen, newlen;
77    int status;
78
79    if (output->type == REMCTL_OUT_ERROR)
80        buffer = &result->error;
81    else if (output->type == REMCTL_OUT_OUTPUT && output->stream == 1) {
82        buffer = &result->stdout_buf;
83        length = &result->stdout_len;
84    } else if (output->type == REMCTL_OUT_OUTPUT && output->stream == 2) {
85        buffer = &result->stderr_buf;
86        length = &result->stderr_len;
87    } else if (output->type == REMCTL_OUT_OUTPUT) {
88        if (result->error != NULL)
89            free(result->error);
90        status = asprintf(&result->error, "bad output stream %d",
91                          output->stream);
92        if (status < 0)
93            result->error = NULL;
94        return false;
95    } else {
96        if (result->error != NULL)
97            free(result->error);
98        result->error = strdup("internal error: bad output type");
99        return false;
100    }
101
102    /* We've done our setup, so now we can do the actual manipulation. */
103    old = *buffer;
104    if (length != NULL)
105        oldlen = *length;
106    else if (old == NULL)
107        oldlen = 0;
108    else
109        oldlen = strlen(old);
110    newlen = oldlen + output->length;
111    if (output->type == REMCTL_OUT_ERROR)
112        newlen++;
113    newbuf = realloc(*buffer, newlen);
114    if (newbuf == NULL) {
115        if (result->error != NULL)
116            free(result->error);
117        result->error = strdup("cannot allocate memory");
118        return false;
119    }
120    *buffer = newbuf;
121    if (length != NULL)
122        *length = newlen;
123    memcpy(*buffer + oldlen, output->data, output->length);
124    if (output->type == REMCTL_OUT_ERROR)
125        (*buffer)[newlen - 1] = '\0';
126    return true;
127}
128
129
130/*
131 * The simplified interface.  Given a host, a port, and a command (as a
132 * null-terminated argv-style vector), run the command on that host and port
133 * and return a struct remctl_result.  The result should be freed with
134 * remctl_result_free.
135 */
136struct remctl_result *
137remctl(const char *host, unsigned short port, const char *principal,
138       const char **command)
139{
140    struct remctl *r = NULL;
141    struct remctl_result *result = NULL;
142    enum remctl_output_type type;
143
144    result = calloc(1, sizeof(struct remctl_result));
145    if (result == NULL)
146        return NULL;
147    r = remctl_new();
148    if (r == NULL)
149        return internal_fail(r, result);
150    if (!remctl_open(r, host, port, principal))
151        return internal_fail(r, result);
152    if (!remctl_command(r, command))
153        return internal_fail(r, result);
154    do {
155        struct remctl_output *output;
156
157        output = remctl_output(r);
158        if (output == NULL)
159            return internal_fail(r, result);
160        type = output->type;
161        if (type == REMCTL_OUT_OUTPUT || type == REMCTL_OUT_ERROR) {
162            if (!internal_output_append(result, output)) {
163                if (result->error != NULL)
164                    return result;
165                else {
166                    remctl_result_free(result);
167                    return NULL;
168                }
169            }
170        } else if (type == REMCTL_OUT_STATUS) {
171            result->status = output->status;
172        } else {
173            remctl_close(r);
174            return result;
175        }
176    } while (type == REMCTL_OUT_OUTPUT);
177    remctl_close(r);
178    return result;
179}
180
181
182/*
183 * Free a struct remctl_result returned by remctl.
184 */
185void
186remctl_result_free(struct remctl_result *result)
187{
188    if (result != NULL) {
189        if (result->error != NULL)
190            free(result->error);
191        if (result->stdout_buf != NULL)
192            free(result->stdout_buf);
193        if (result->stderr_buf != NULL)
194            free(result->stderr_buf);
195        free(result);
196    }
197}
198
199
200/*
201 * Create a new remctl object.  Don't attempt to connect here; we do that
202 * separately so that we can still use the object to store error messages
203 * from connection failures.  Return NULL on memory allocation failures or
204 * socket initialization failures (Windows).
205 */
206struct remctl *
207remctl_new(void)
208{
209    struct remctl *r;
210
211    if (!socket_init())
212        return NULL;
213    r = calloc(1, sizeof(struct remctl));
214    if (r == NULL)
215        return NULL;
216    r->fd = -1;
217    r->host = NULL;
218    r->principal = NULL;
219    r->context = NULL;
220    r->error = NULL;
221    r->output = NULL;
222    return r;
223}
224
225
226/*
227 * Open a new persistant remctl connection to a server, given the host, port,
228 * and principal.  Returns true on success and false on failure.
229 */
230int
231remctl_open(struct remctl *r, const char *host, unsigned short port,
232            const char *principal)
233{
234    if (r->fd != -1)
235        socket_close(r->fd);
236    if (r->error != NULL) {
237        free(r->error);
238        r->error = NULL;
239    }
240    if (r->output != NULL) {
241        internal_output_wipe(r->output);
242        free(r->output);
243        r->output = NULL;
244    }
245    r->host = host;
246    r->port = port;
247    r->principal = principal;
248    return internal_open(r, host, port, principal);
249}
250
251
252/*
253 * Close a persistant remctl connection.
254 */
255void
256remctl_close(struct remctl *r)
257{
258    if (r != NULL) {
259        if (r->protocol > 1 && r->fd != -1)
260            internal_v2_quit(r);
261        if (r->fd != -1)
262            socket_close(r->fd);
263        if (r->error != NULL)
264            free(r->error);
265        if (r->output != NULL)
266            free(r->output);
267        free(r);
268    }
269    socket_shutdown();
270}
271
272
273/*
274 * Send a complete remote command.  Returns true on success, false on failure.
275 * On failure, use remctl_error to get the error.  command is a
276 * NULL-terminated array of nul-terminated strings.  finished is a boolean
277 * flag that should be false when the command is not yet complete and true
278 * when this is the final (or only) segment.
279 *
280 * Implement in terms of remctl_commandv.
281 */
282int
283remctl_command(struct remctl *r, const char **command)
284{
285    struct iovec *vector;
286    size_t count, i;
287    int status;
288
289    for (count = 0; command[count] != NULL; count++)
290        ;
291    vector = malloc(sizeof(struct iovec) * count);
292    if (vector == NULL) {
293        internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
294        return 0;
295    }
296    for (i = 0; i < count; i++) {
297        vector[i].iov_base = (void *) command[i];
298        vector[i].iov_len = strlen(command[i]);
299    }
300    status = remctl_commandv(r, vector, count);
301    free(vector);
302    return status;
303}
304
305
306/*
307 * Same as remctl_command, but take the command as an array of struct iovecs
308 * instead.  Use this form for binary data.
309 */
310int
311remctl_commandv(struct remctl *r, const struct iovec *command, size_t count)
312{
313    if (r->fd < 0) {
314        if (r->host == NULL) {
315            internal_set_error(r, "no connection open");
316            return 0;
317        }
318        if (!remctl_open(r, r->host, r->port, r->principal))
319            return 0;
320    }
321    if (r->error != NULL) {
322        free(r->error);
323        r->error = NULL;
324    }
325    if (r->protocol == 1)
326        return internal_v1_commandv(r, command, count);
327    else
328        return internal_v2_commandv(r, command, count);
329}
330
331
332/*
333 * Helper function for remctl_output implementations.  Free and reset the
334 * elements of the output struct, but don't free the output struct itself.
335 */
336void
337internal_output_wipe(struct remctl_output *output)
338{
339    if (output == NULL)
340        return;
341    output->type = REMCTL_OUT_DONE;
342    if (output->data != NULL) {
343        free(output->data);
344        output->data = NULL;
345    }
346    output->length = 0;
347    output->stream = 0;
348    output->status = 0;
349    output->error = 0;
350}
351
352
353/*
354 * Retrieve output from the remote server.  Each call to this function on the
355 * same connection invalidates the previous returned remctl_output struct.
356 *
357 * This function will return zero or more REMCTL_OUT_OUTPUT types followed by
358 * a REMCTL_OUT_STATUS type, *or* a REMCTL_OUT_ERROR type.  In either case,
359 * any subsequent call before sending a new command will return
360 * REMCTL_OUT_DONE.  If the function returns NULL, an internal error occurred;
361 * call remctl_error to retrieve the error message.
362 *
363 * The remctl_output struct should *not* be freed by the caller.  It will be
364 * invalidated after another call to remctl_output or to remctl_close on the
365 * same connection.
366 */
367struct remctl_output *
368remctl_output(struct remctl *r)
369{
370    if (r->fd < 0 && (r->protocol != 1 || r->host == NULL)) {
371        internal_set_error(r, "no connection open");
372        return NULL;
373    }
374    if (r->error != NULL) {
375        free(r->error);
376        r->error = NULL;
377    }
378    if (r->protocol == 1)
379        return internal_v1_output(r);
380    else
381        return internal_v2_output(r);
382}
383
384
385/*
386 * Returns the internal error message after a failure or "no error" if the
387 * last command completed successfully.  This should generally only be called
388 * after a failure.
389 */
390const char *
391remctl_error(struct remctl *r)
392{
393    return (r->error != NULL) ? r->error : "no error";
394}
Note: See TracBrowser for help on using the repository browser.