~cpick/mongrel2/release

« back to all changes in this revision

Viewing changes to tools/m2sh/src/commands/running.c

  • Committer: Chris Pick
  • Date: 2013-06-30 16:39:57 UTC
  • mfrom: (1106.1.15)
  • Revision ID: git-v1:ec39967acb6bc9867ed9b9dc3774304ca6b9c294
Merge tag 'v1.8.1' into debian

Hotfix for github issue 148

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 *
 
3
 * Copyright (c) 2010, Zed A. Shaw and Mongrel2 Project Contributors.
 
4
 * All rights reserved.
 
5
 * 
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions are
 
8
 * met:
 
9
 * 
 
10
 *     * Redistributions of source code must retain the above copyright
 
11
 *       notice, this list of conditions and the following disclaimer.
 
12
 * 
 
13
 *     * Redistributions in binary form must reproduce the above copyright
 
14
 *       notice, this list of conditions and the following disclaimer in the
 
15
 *       documentation and/or other materials provided with the distribution.
 
16
 * 
 
17
 *     * Neither the name of the Mongrel2 Project, Zed A. Shaw, nor the names
 
18
 *       of its contributors may be used to endorse or promote products
 
19
 *       derived from this software without specific prior written
 
20
 *       permission.
 
21
 * 
 
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 
23
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
24
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
25
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 
26
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
27
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
28
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
29
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 
30
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 
31
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
32
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
33
 */
 
34
 
 
35
#include <signal.h>
 
36
 
 
37
#include <zmq.h>
 
38
 
 
39
#include <tnetstrings.h>
 
40
#include <tnetstrings_impl.h>
 
41
#include <config/db.h>
 
42
#include <handler.h>
 
43
#include <pattern.h>
 
44
#include <register.h>
 
45
 
 
46
#include "../linenoise.h"
 
47
#include "../commands.h"
 
48
#include "../query_print.h"
 
49
#include "logging.h"
 
50
#include "running.h"
 
51
 
 
52
struct ServerRun {
 
53
    int ran;
 
54
    bstring db_file;
 
55
    bstring config_url;
 
56
    bstring config_style;
 
57
    bstring uuid;
 
58
    const char *sudo;
 
59
    int murder;
 
60
};
 
61
 
 
62
static inline int exec_server_operations(Command *cmd,
 
63
        int (*callback)(struct ServerRun *, tns_value_t *), const char *select)
 
64
{
 
65
    int rc = 0;
 
66
    tns_value_t *res = NULL;
 
67
 
 
68
    bstring db_file = option(cmd, "db", NULL);
 
69
    bstring conf_url = option(cmd, "url", NULL);
 
70
    bstring conf_style = option(cmd, "style", NULL);
 
71
    bstring uuid = option(cmd, "uuid", NULL);
 
72
 
 
73
    if(conf_url == NULL && db_file == NULL) {
 
74
        db_file = bfromcstr("config.sqlite");
 
75
    }
 
76
 
 
77
    check(db_file != NULL || (conf_url != NULL && conf_style != NULL),
 
78
            "You must give either --db or --style and --url.");
 
79
 
 
80
    struct ServerRun run = {
 
81
        .ran = 0,
 
82
        .db_file = db_file,
 
83
        .config_url = conf_url,
 
84
        .config_style = conf_style,
 
85
        .sudo = "",
 
86
        .uuid = uuid,
 
87
        .murder = 0
 
88
    };
 
89
 
 
90
    bstring name = option(cmd, "name", NULL);
 
91
    bstring host = option(cmd, "host", NULL);
 
92
    bstring sudo = option(cmd, "sudo", NULL);
 
93
    bstring every = option(cmd, "every", NULL);
 
94
    run.murder = option(cmd, "murder", NULL) != NULL;
 
95
 
 
96
    check(!(name && uuid && host), "Just one please, not all of the options.");
 
97
 
 
98
    if(sudo) {
 
99
        run.sudo = biseqcstr(sudo, "") ? "sudo" : bdata(sudo);
 
100
    } else {
 
101
        run.sudo = "";
 
102
    }
 
103
 
 
104
    if(uuid == NULL) {
 
105
        check(db_file != NULL, "There is no uuid, and no database given, need a uuid.");
 
106
 
 
107
        rc = DB_init(bdata(db_file));
 
108
        check(rc == 0, "Failed to open db: %s", bdata(db_file));
 
109
 
 
110
        if(every) {
 
111
            res = DB_exec("SELECT %s FROM server", select);
 
112
        } else if(name) {
 
113
            res = DB_exec("SELECT %s FROM server where name = %Q", select, bdata(name));
 
114
        } else if(host) {
 
115
            res = DB_exec("SELECT %s FROM server where default_host = %Q", select, bdata(host));
 
116
        } else if(uuid) {
 
117
            // yes, this is necessary, so that the callback runs
 
118
            res = DB_exec("SELECT %s FROM server where uuid = %Q", select, bdata(uuid));
 
119
        } else {
 
120
            sentinel("You must give either -name, -uuid, or -host to start a server.");
 
121
        }
 
122
 
 
123
        check(tns_get_type(res) == tns_tag_list,
 
124
                "Wrong return type from query, should be list.");
 
125
    }
 
126
 
 
127
    check(callback(&run, res) != -1, "Failed to run internal operation.");
 
128
 
 
129
    if(!run.ran) {
 
130
        errno = 0;
 
131
 
 
132
        if(every) {
 
133
            log_err("You specified -every server but didn't load any. Not configured right?");
 
134
        } else if(host) {
 
135
            log_err("Could not load server with host '%s' from db %s.", bdata(host), bdata(db_file));
 
136
        } else if(uuid) {
 
137
            log_err("Could not load server with uuid '%s' from db %s.", bdata(uuid), bdata(db_file));
 
138
        } else if(name) {
 
139
            log_err("Could not load server named '%s' from db %s.", bdata(name), bdata(db_file));
 
140
        } else {
 
141
            log_err("Well looks like you broke something, please report what you did to mongrel2.org.");
 
142
        }
 
143
 
 
144
        sentinel("Error loading the requested server, see above for why.");
 
145
    }
 
146
 
 
147
    if(res) tns_value_destroy(res);
 
148
    DB_close();
 
149
    return 0;
 
150
 
 
151
error:
 
152
    if(res) tns_value_destroy(res);
 
153
    DB_close();
 
154
    return -1;
 
155
}
 
156
 
 
157
static int run_server(struct ServerRun *r, tns_value_t *res)
 
158
{
 
159
    r->ran = 0;
 
160
    bstring config = NULL;
 
161
    bstring module = NULL;
 
162
    bstring uuid = NULL;
 
163
 
 
164
    if(r->db_file) {
 
165
        DB_check(res, 0, 1, tns_tag_string);
 
166
        tns_value_t *uuid_val = DB_get(res, 0, 0);
 
167
 
 
168
        config = bstrcpy(r->db_file);
 
169
        module = bfromcstr("");
 
170
        uuid = bstrcpy(uuid_val->value.string);
 
171
    } else {
 
172
        config = bstrcpy(r->config_url);
 
173
        module = bstrcpy(r->config_style);
 
174
        uuid = bstrcpy(r->uuid);
 
175
    }
 
176
 
 
177
    bstring command = bformat("%s mongrel2 %s %s %s",
 
178
            r->sudo, bdata(config), bdata(uuid), bdata(module));
 
179
 
 
180
    system(bdata(command));
 
181
 
 
182
    bdestroy(command);
 
183
    bdestroy(config);
 
184
    bdestroy(module);
 
185
 
 
186
    r->ran = 1;
 
187
    return 0;
 
188
 
 
189
error:
 
190
    return -1;
 
191
}
 
192
 
 
193
int Command_start(Command *cmd)
 
194
{
 
195
    return exec_server_operations(cmd, run_server, "uuid");
 
196
}
 
197
 
 
198
bstring read_pid_file(bstring pid_path)
 
199
{
 
200
    FILE *pid_file = fopen(bdata(pid_path), "r");
 
201
    bstring pid = NULL;
 
202
 
 
203
    if(pid_file == NULL) {
 
204
        return NULL;
 
205
    } else {
 
206
        pid = bread((bNread)fread, pid_file);
 
207
        fclose(pid_file); pid_file = NULL;
 
208
    }
 
209
 
 
210
    return pid;
 
211
}
 
212
 
 
213
static int locate_pid_file(tns_value_t *res)
 
214
{
 
215
    bstring pid = NULL;
 
216
    bstring pid_path = NULL;
 
217
 
 
218
    int cols = 0;
 
219
    int rows = DB_counts(res, &cols);
 
220
    check(rows == 1 && cols == 2, "Wrong number of results.");
 
221
 
 
222
    tns_value_t *chroot = DB_get(res, 0, 0);
 
223
    check(tns_get_type(chroot) == tns_tag_string, "Wrong result for server chroot, should be a string.");
 
224
 
 
225
    tns_value_t *pid_file = DB_get(res, 0, 1);
 
226
    check(tns_get_type(pid_file) == tns_tag_string, "Wrong result for server pid_file, should be a string.");
 
227
 
 
228
    pid_path = bformat("%s%s", bdata(chroot->value.string), bdata(pid_file->value.string));
 
229
 
 
230
    pid = read_pid_file(pid_path);
 
231
    check(pid, "Couldn't read the PID from %s", bdata(pid_path));
 
232
 
 
233
    int result = atoi((const char *)pid->data);
 
234
 
 
235
    bdestroy(pid);
 
236
    bdestroy(pid_path);
 
237
    return result;
 
238
 
 
239
error:
 
240
    bdestroy(pid);
 
241
    bdestroy(pid_path);
 
242
    return -1;
 
243
}
 
244
 
 
245
static int kill_server(struct ServerRun *r, tns_value_t *res, int signal)
 
246
{
 
247
    int pid = locate_pid_file(res);
 
248
    check(pid != -1, "Failed to read the pid_file.");
 
249
 
 
250
    int rc = kill(pid, signal);
 
251
    check(rc == 0, "Failed to stop server with PID: %d", pid);
 
252
 
 
253
    r->ran = 1;
 
254
    return 0;
 
255
 
 
256
error:
 
257
    r->ran = 0;
 
258
    return -1;
 
259
}
 
260
 
 
261
 
 
262
static int stop_server(struct ServerRun *r, tns_value_t *res)
 
263
{
 
264
    int signal = r->murder ? SIGTERM : SIGINT;
 
265
    return kill_server(r, res, signal);
 
266
}
 
267
 
 
268
int Command_stop(Command *cmd)
 
269
{
 
270
    return exec_server_operations(cmd, stop_server, "chroot, pid_file");
 
271
}
 
272
 
 
273
static int reload_server(struct ServerRun *r, tns_value_t *res)
 
274
{
 
275
    return kill_server(r, res, SIGHUP);
 
276
}
 
277
 
 
278
int Command_reload(Command *cmd)
 
279
{
 
280
    return exec_server_operations(cmd, reload_server, "chroot, pid_file");
 
281
}
 
282
 
 
283
static int check_server(struct ServerRun *r, tns_value_t *res)
 
284
{
 
285
    int rc = 0;
 
286
    int pid = locate_pid_file(res);
 
287
 
 
288
    if(pid == -1) {
 
289
        printf("mongrel2 is not running because pid_file isn't there.\n");
 
290
        r->ran = 1;
 
291
        return 0;
 
292
    }
 
293
 
 
294
    errno = 0;
 
295
    rc = kill(pid, 0);
 
296
 
 
297
    if((rc != 0) && (errno == ESRCH)) {
 
298
        printf("mongrel2 at PID %d is NOT running.\n", pid);
 
299
    } else if ((rc == 0) || (errno == EPERM)) {
 
300
        printf("mongrel2 at PID %d running.\n", pid);
 
301
    } else {
 
302
        sentinel("Could not send signal to mongrel2 at PID %d", pid);
 
303
    }
 
304
 
 
305
    r->ran = 1;
 
306
    return 0;
 
307
 
 
308
error:
 
309
    r->ran = 0;
 
310
    return -1;
 
311
}
 
312
 
 
313
int Command_running(Command *cmd)
 
314
{
 
315
    return exec_server_operations(cmd, check_server, "chroot, pid_file");
 
316
}
 
317
 
 
318
static inline int linenoise_runner(const char *prompt, int (*callback)(bstring arg, void *data), void *data)
 
319
{
 
320
    char *line = NULL;
 
321
    bstring args = NULL;
 
322
    char *home_dir = getenv("HOME");
 
323
    bstring hist_file = NULL;
 
324
 
 
325
    if(home_dir != NULL) {
 
326
        hist_file = bformat("%s/.m2sh", home_dir);
 
327
        linenoiseHistoryLoad(bdata(hist_file));
 
328
    } else {
 
329
        log_warn("You don't have a HOME environment variable. Oh well, no history.");
 
330
        hist_file = NULL;
 
331
    }
 
332
 
 
333
    while((line = linenoise(prompt)) != NULL) {
 
334
        if (line[0] != '\0') {
 
335
            args = bformat("%s", line);
 
336
            callback(args, data);
 
337
            bdestroy(args);
 
338
 
 
339
            if(hist_file) {
 
340
                linenoiseHistoryAdd(line);
 
341
                linenoiseHistorySave(bdata(hist_file)); /* Save every new entry */
 
342
            }
 
343
        }
 
344
 
 
345
        free(line);
 
346
    }
 
347
 
 
348
    bdestroy(hist_file);
 
349
    return 0;
 
350
}
 
351
 
 
352
static struct tagbstring TOKENS = bsStatic(" \t\n=");
 
353
static struct tagbstring NUMBER_PATTERN = bsStatic("[\\-0-9]+");
 
354
bstring parse_input(bstring inbuf)
 
355
{
 
356
    size_t len = 0;
 
357
    int i = 0;
 
358
    char *data = NULL;
 
359
    bstring result = NULL;
 
360
    tns_value_t *req = tns_new_list();
 
361
    tns_value_t *args = tns_new_dict();
 
362
    tns_value_t *value = NULL;
 
363
 
 
364
    btrimws(inbuf);
 
365
    struct bstrList *list = bsplits(inbuf, &TOKENS);
 
366
    check((list->qty + 1) % 2 == 0, "USAGE: command arg1=val1 arg2=val2");
 
367
 
 
368
    tns_add_to_list(req,
 
369
            tns_parse_string(bdata(list->entry[0]), blength(list->entry[0])));
 
370
 
 
371
    for(i = 1; i < list->qty; i += 2) {
 
372
        bstring key_str = list->entry[i];
 
373
        bstring val_str = list->entry[i+1];
 
374
        tns_value_t *key = tns_parse_string(bdata(key_str), blength(key_str));
 
375
 
 
376
        if(bstring_match(val_str, &NUMBER_PATTERN)) {
 
377
            value = tns_parse_integer(bdata(val_str), blength(val_str));
 
378
        } else {
 
379
            value = tns_parse_string(bdata(val_str), blength(val_str));
 
380
        }
 
381
 
 
382
        tns_add_to_dict(args, key, value);
 
383
    }
 
384
 
 
385
    tns_add_to_list(req, args);
 
386
 
 
387
    data = tns_render(req, &len);
 
388
    check(data != NULL, "Didn't render to a valid TNetstring.");
 
389
    check(len > 0, "Didn't create a valid TNetstring.");
 
390
 
 
391
    result = blk2bstr(data, len);
 
392
    check(result != NULL, "Couldn't convert to string.");
 
393
 
 
394
    tns_value_destroy(req);
 
395
    bstrListDestroy(list);
 
396
    free(data);
 
397
    return result;
 
398
 
 
399
error:
 
400
 
 
401
    if(req) tns_value_destroy(req);
 
402
    if(list) bstrListDestroy(list);
 
403
    if(data) free(data);
 
404
    if(result) bdestroy(result);
 
405
 
 
406
    return NULL;
 
407
}
 
408
 
 
409
static void bstring_free(void *data, void *hint)
 
410
{
 
411
    bdestroy((bstring)hint);
 
412
}
 
413
 
 
414
struct tagbstring ERROR_KEY = bsStatic("error");
 
415
struct tagbstring HEADERS_KEY = bsStatic("headers");
 
416
struct tagbstring ROWS_KEY = bsStatic("rows");
 
417
 
 
418
static void display_map_style(tns_value_t *headers, tns_value_t *table)
 
419
{
 
420
    int i = 0;
 
421
    int cols = 0;
 
422
    int rows = DB_counts(table, &cols);
 
423
    check(rows != -1, "Invalid query result, probably not a table.");
 
424
    darray_t *names = headers->value.list;
 
425
 
 
426
    check(cols == darray_end(names),
 
427
            "Server returned a bad result, names isn't same length as elements.");
 
428
 
 
429
    if(rows == 1) {
 
430
        for(i = 0; i < cols; i++)
 
431
        {
 
432
            tns_value_t *h = darray_get(names, i);
 
433
            tns_value_t *val = DB_get(table, 0, i);
 
434
            check(tns_get_type(h) == tns_tag_string, "Headers should be strings.");
 
435
            check(tns_get_type(val) != tns_tag_invalid,
 
436
                    "Invalid value for column %d of result.", i);
 
437
 
 
438
            print_datum(h);
 
439
            printf(":  ");
 
440
            print_datum(val);
 
441
            printf("\n");
 
442
        }
 
443
    } else {
 
444
        sentinel("Asked to display something that's not in map style.");
 
445
    }
 
446
 
 
447
error: // fallthrough
 
448
    return;
 
449
}
 
450
 
 
451
 
 
452
void display_table_style(tns_value_t *headers, tns_value_t *table)
 
453
{
 
454
    int col_i = 0;
 
455
    int row_i = 0;
 
456
    for(col_i = 0; col_i < darray_end(headers->value.list); col_i++)
 
457
    {
 
458
        tns_value_t *h = darray_get(headers->value.list, col_i);
 
459
        check(tns_get_type(h) == tns_tag_string,
 
460
                "Headers should be strings, not: %c", tns_get_type(h));
 
461
        printf("%s  ", bdata(h->value.string));
 
462
    }
 
463
    printf("\n");
 
464
 
 
465
    int cols = 0;
 
466
    int rows = DB_counts(table, &cols);
 
467
    check(rows != -1, "Invalid query results, probably not in table format.");
 
468
 
 
469
    for(row_i = 0; row_i < rows; row_i++) {
 
470
        for(col_i = 0; col_i < cols; col_i++) {
 
471
            tns_value_t *col = DB_get(table, row_i, col_i);
 
472
            print_datum(col);
 
473
            printf("  ");
 
474
        }
 
475
        printf("\n");
 
476
    }
 
477
    printf("\n");
 
478
 
 
479
error: // fallthrough
 
480
    return;
 
481
}
 
482
 
 
483
void display_response(const char *msg, size_t len)
 
484
{
 
485
    tns_value_t *resp = tns_parse(msg, len, NULL);
 
486
    hnode_t *node = NULL;
 
487
 
 
488
    check(tns_get_type(resp) == tns_tag_dict, "Server returned an invalid response, must be a dict.");
 
489
 
 
490
    node = hash_lookup(resp->value.dict, &ERROR_KEY);
 
491
 
 
492
    if(node) {
 
493
        tns_value_t *val = hnode_get(node);
 
494
        printf("ERROR: %s\n", bdata(val->value.string));
 
495
    } else {
 
496
        node = hash_lookup(resp->value.dict, &HEADERS_KEY);
 
497
        check(node != NULL, "Server returned an invalid response, need a 'headers'.");
 
498
        tns_value_t *headers = hnode_get(node);
 
499
        check(tns_get_type(headers) == tns_tag_list, "Headers must be a list, server is screwed up.");
 
500
 
 
501
        node = hash_lookup(resp->value.dict, &ROWS_KEY);
 
502
        check(node != NULL, "Server returned an invalid response, need a 'rows'.");
 
503
        tns_value_t *rows = hnode_get(node);
 
504
        check(tns_get_type(rows) == tns_tag_list, "Rows must be a list, server is screwed up.");
 
505
 
 
506
        if(darray_end(rows->value.list) == 1) {
 
507
            display_map_style(headers, rows);
 
508
        } else {
 
509
            display_table_style(headers, rows);
 
510
        }
 
511
    }
 
512
 
 
513
error: // fallthrough
 
514
    tns_value_destroy(resp);
 
515
    return;
 
516
}
 
517
 
 
518
int send_recv_control(bstring args, void *socket)
 
519
{
 
520
    int rc = 0;
 
521
    zmq_msg_t *outmsg = NULL;
 
522
    zmq_msg_t *inmsg = NULL;
 
523
 
 
524
    outmsg = calloc(sizeof(zmq_msg_t), 1);
 
525
    check_mem(outmsg);
 
526
    inmsg = calloc(sizeof(zmq_msg_t), 1);
 
527
    check_mem(inmsg);
 
528
    
 
529
    rc = zmq_msg_init(outmsg);
 
530
    check(rc == 0, "Failed to initialize outgoing message.");
 
531
    rc = zmq_msg_init(inmsg);
 
532
    check(rc == 0, "Failed to initialize incoming message.");
 
533
 
 
534
    bstring request = parse_input(args);
 
535
    check(request != NULL, "Invalid command, try again.");
 
536
 
 
537
    // send the message
 
538
    rc = zmq_msg_init_data(outmsg, bdata(request), blength(request)+1, bstring_free, request);
 
539
    check(rc == 0, "Failed to init outgoing message.");
 
540
 
 
541
    rc = mqsend(socket, outmsg, 0);
 
542
    check(rc == 0, "Failed to send message to control port.");
 
543
    free(outmsg);
 
544
 
 
545
    // recv the response
 
546
    rc = mqrecv(socket, inmsg, 0);
 
547
    check(rc == 0, "Failed to receive message from control port.");
 
548
 
 
549
    display_response((const char *)zmq_msg_data(inmsg), zmq_msg_size(inmsg));
 
550
 
 
551
    fflush(stdout);
 
552
    free(inmsg);
 
553
 
 
554
    return 0;
 
555
 
 
556
error:
 
557
    if(outmsg) free(outmsg);
 
558
    if(inmsg) free(inmsg);
 
559
    return -1;
 
560
}
 
561
 
 
562
int control_server(struct ServerRun *r, tns_value_t *res)
 
563
{
 
564
    int rc = 0;
 
565
    void *socket = NULL;
 
566
    bstring prompt = NULL;
 
567
    bstring control = NULL;
 
568
    r->ran = 1;
 
569
    int cols = 0;
 
570
    int rows = DB_counts(res, &cols);
 
571
 
 
572
    check(rows != -1, "Invalid data given to internal routine control_server.");
 
573
    check(rows == 1, "Ambiguous server select, expected 1 but got %d.", rows);
 
574
 
 
575
    bstring server_name = DB_get_as(res, 0, 0, string);
 
576
    bstring chroot = DB_get_as(res, 0, 1, string);
 
577
 
 
578
    check(server_name != NULL && chroot != NULL, 
 
579
            "Somehow didn't get a good server_name and chroot.");
 
580
 
 
581
    prompt = bformat("m2 [%s]> ", bdata(server_name));
 
582
    control = bformat("ipc://%s/run/control", bdata(chroot));
 
583
    log_info("Connecting to control port %s", bdata(control));
 
584
 
 
585
    mqinit(1);
 
586
    Register_init();
 
587
 
 
588
    socket = mqsocket(ZMQ_REQ);
 
589
    check(socket != NULL, "Failed to create REQ socket.");
 
590
 
 
591
    rc = zmq_connect(socket, bdata(control));
 
592
    check(rc == 0, "Failed to connect to control port.");
 
593
 
 
594
    rc = linenoise_runner(bdata(prompt), send_recv_control, socket);
 
595
 
 
596
    bdestroy(prompt);
 
597
    bdestroy(control);
 
598
    zmq_close(socket);
 
599
    zmq_term(ZMQ_CTX);
 
600
    return rc;
 
601
 
 
602
error:
 
603
    if(prompt) bdestroy(prompt);
 
604
    if(control) bdestroy(control);
 
605
    if(socket) zmq_close(socket);
 
606
    return -1;
 
607
}
 
608
 
 
609
int Command_control(Command *cmd)
 
610
{
 
611
    return exec_server_operations(cmd, control_server, "name, chroot");
 
612
}
 
613
 
 
614
static int run_command(bstring line, void *ignored)
 
615
{
 
616
    bstring args = bformat("m2sh %s", bdata(line));
 
617
    int rc = Command_run(args);
 
618
 
 
619
    bdestroy(args);
 
620
    return rc;
 
621
}
 
622
 
 
623
int Command_shell(Command *cmd)
 
624
{
 
625
    return linenoise_runner("mongrel2> ", run_command, NULL);
 
626
}
 
627