3
* Copyright (c) 2010, Zed A. Shaw and Mongrel2 Project Contributors.
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions are
10
* * Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
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.
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
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.
39
#include <tnetstrings.h>
40
#include <tnetstrings_impl.h>
41
#include <config/db.h>
46
#include "../linenoise.h"
47
#include "../commands.h"
48
#include "../query_print.h"
62
static inline int exec_server_operations(Command *cmd,
63
int (*callback)(struct ServerRun *, tns_value_t *), const char *select)
66
tns_value_t *res = NULL;
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);
73
if(conf_url == NULL && db_file == NULL) {
74
db_file = bfromcstr("config.sqlite");
77
check(db_file != NULL || (conf_url != NULL && conf_style != NULL),
78
"You must give either --db or --style and --url.");
80
struct ServerRun run = {
83
.config_url = conf_url,
84
.config_style = conf_style,
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;
96
check(!(name && uuid && host), "Just one please, not all of the options.");
99
run.sudo = biseqcstr(sudo, "") ? "sudo" : bdata(sudo);
105
check(db_file != NULL, "There is no uuid, and no database given, need a uuid.");
107
rc = DB_init(bdata(db_file));
108
check(rc == 0, "Failed to open db: %s", bdata(db_file));
111
res = DB_exec("SELECT %s FROM server", select);
113
res = DB_exec("SELECT %s FROM server where name = %Q", select, bdata(name));
115
res = DB_exec("SELECT %s FROM server where default_host = %Q", select, bdata(host));
117
// yes, this is necessary, so that the callback runs
118
res = DB_exec("SELECT %s FROM server where uuid = %Q", select, bdata(uuid));
120
sentinel("You must give either -name, -uuid, or -host to start a server.");
123
check(tns_get_type(res) == tns_tag_list,
124
"Wrong return type from query, should be list.");
127
check(callback(&run, res) != -1, "Failed to run internal operation.");
133
log_err("You specified -every server but didn't load any. Not configured right?");
135
log_err("Could not load server with host '%s' from db %s.", bdata(host), bdata(db_file));
137
log_err("Could not load server with uuid '%s' from db %s.", bdata(uuid), bdata(db_file));
139
log_err("Could not load server named '%s' from db %s.", bdata(name), bdata(db_file));
141
log_err("Well looks like you broke something, please report what you did to mongrel2.org.");
144
sentinel("Error loading the requested server, see above for why.");
147
if(res) tns_value_destroy(res);
152
if(res) tns_value_destroy(res);
157
static int run_server(struct ServerRun *r, tns_value_t *res)
160
bstring config = NULL;
161
bstring module = NULL;
165
DB_check(res, 0, 1, tns_tag_string);
166
tns_value_t *uuid_val = DB_get(res, 0, 0);
168
config = bstrcpy(r->db_file);
169
module = bfromcstr("");
170
uuid = bstrcpy(uuid_val->value.string);
172
config = bstrcpy(r->config_url);
173
module = bstrcpy(r->config_style);
174
uuid = bstrcpy(r->uuid);
177
bstring command = bformat("%s mongrel2 %s %s %s",
178
r->sudo, bdata(config), bdata(uuid), bdata(module));
180
system(bdata(command));
193
int Command_start(Command *cmd)
195
return exec_server_operations(cmd, run_server, "uuid");
198
bstring read_pid_file(bstring pid_path)
200
FILE *pid_file = fopen(bdata(pid_path), "r");
203
if(pid_file == NULL) {
206
pid = bread((bNread)fread, pid_file);
207
fclose(pid_file); pid_file = NULL;
213
static int locate_pid_file(tns_value_t *res)
216
bstring pid_path = NULL;
219
int rows = DB_counts(res, &cols);
220
check(rows == 1 && cols == 2, "Wrong number of results.");
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.");
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.");
228
pid_path = bformat("%s%s", bdata(chroot->value.string), bdata(pid_file->value.string));
230
pid = read_pid_file(pid_path);
231
check(pid, "Couldn't read the PID from %s", bdata(pid_path));
233
int result = atoi((const char *)pid->data);
245
static int kill_server(struct ServerRun *r, tns_value_t *res, int signal)
247
int pid = locate_pid_file(res);
248
check(pid != -1, "Failed to read the pid_file.");
250
int rc = kill(pid, signal);
251
check(rc == 0, "Failed to stop server with PID: %d", pid);
262
static int stop_server(struct ServerRun *r, tns_value_t *res)
264
int signal = r->murder ? SIGTERM : SIGINT;
265
return kill_server(r, res, signal);
268
int Command_stop(Command *cmd)
270
return exec_server_operations(cmd, stop_server, "chroot, pid_file");
273
static int reload_server(struct ServerRun *r, tns_value_t *res)
275
return kill_server(r, res, SIGHUP);
278
int Command_reload(Command *cmd)
280
return exec_server_operations(cmd, reload_server, "chroot, pid_file");
283
static int check_server(struct ServerRun *r, tns_value_t *res)
286
int pid = locate_pid_file(res);
289
printf("mongrel2 is not running because pid_file isn't there.\n");
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);
302
sentinel("Could not send signal to mongrel2 at PID %d", pid);
313
int Command_running(Command *cmd)
315
return exec_server_operations(cmd, check_server, "chroot, pid_file");
318
static inline int linenoise_runner(const char *prompt, int (*callback)(bstring arg, void *data), void *data)
322
char *home_dir = getenv("HOME");
323
bstring hist_file = NULL;
325
if(home_dir != NULL) {
326
hist_file = bformat("%s/.m2sh", home_dir);
327
linenoiseHistoryLoad(bdata(hist_file));
329
log_warn("You don't have a HOME environment variable. Oh well, no history.");
333
while((line = linenoise(prompt)) != NULL) {
334
if (line[0] != '\0') {
335
args = bformat("%s", line);
336
callback(args, data);
340
linenoiseHistoryAdd(line);
341
linenoiseHistorySave(bdata(hist_file)); /* Save every new entry */
352
static struct tagbstring TOKENS = bsStatic(" \t\n=");
353
static struct tagbstring NUMBER_PATTERN = bsStatic("[\\-0-9]+");
354
bstring parse_input(bstring inbuf)
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;
365
struct bstrList *list = bsplits(inbuf, &TOKENS);
366
check((list->qty + 1) % 2 == 0, "USAGE: command arg1=val1 arg2=val2");
369
tns_parse_string(bdata(list->entry[0]), blength(list->entry[0])));
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));
376
if(bstring_match(val_str, &NUMBER_PATTERN)) {
377
value = tns_parse_integer(bdata(val_str), blength(val_str));
379
value = tns_parse_string(bdata(val_str), blength(val_str));
382
tns_add_to_dict(args, key, value);
385
tns_add_to_list(req, args);
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.");
391
result = blk2bstr(data, len);
392
check(result != NULL, "Couldn't convert to string.");
394
tns_value_destroy(req);
395
bstrListDestroy(list);
401
if(req) tns_value_destroy(req);
402
if(list) bstrListDestroy(list);
404
if(result) bdestroy(result);
409
static void bstring_free(void *data, void *hint)
411
bdestroy((bstring)hint);
414
struct tagbstring ERROR_KEY = bsStatic("error");
415
struct tagbstring HEADERS_KEY = bsStatic("headers");
416
struct tagbstring ROWS_KEY = bsStatic("rows");
418
static void display_map_style(tns_value_t *headers, tns_value_t *table)
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;
426
check(cols == darray_end(names),
427
"Server returned a bad result, names isn't same length as elements.");
430
for(i = 0; i < cols; i++)
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);
444
sentinel("Asked to display something that's not in map style.");
447
error: // fallthrough
452
void display_table_style(tns_value_t *headers, tns_value_t *table)
456
for(col_i = 0; col_i < darray_end(headers->value.list); col_i++)
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));
466
int rows = DB_counts(table, &cols);
467
check(rows != -1, "Invalid query results, probably not in table format.");
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);
479
error: // fallthrough
483
void display_response(const char *msg, size_t len)
485
tns_value_t *resp = tns_parse(msg, len, NULL);
486
hnode_t *node = NULL;
488
check(tns_get_type(resp) == tns_tag_dict, "Server returned an invalid response, must be a dict.");
490
node = hash_lookup(resp->value.dict, &ERROR_KEY);
493
tns_value_t *val = hnode_get(node);
494
printf("ERROR: %s\n", bdata(val->value.string));
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.");
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.");
506
if(darray_end(rows->value.list) == 1) {
507
display_map_style(headers, rows);
509
display_table_style(headers, rows);
513
error: // fallthrough
514
tns_value_destroy(resp);
518
int send_recv_control(bstring args, void *socket)
521
zmq_msg_t *outmsg = NULL;
522
zmq_msg_t *inmsg = NULL;
524
outmsg = calloc(sizeof(zmq_msg_t), 1);
526
inmsg = calloc(sizeof(zmq_msg_t), 1);
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.");
534
bstring request = parse_input(args);
535
check(request != NULL, "Invalid command, try again.");
538
rc = zmq_msg_init_data(outmsg, bdata(request), blength(request)+1, bstring_free, request);
539
check(rc == 0, "Failed to init outgoing message.");
541
rc = mqsend(socket, outmsg, 0);
542
check(rc == 0, "Failed to send message to control port.");
546
rc = mqrecv(socket, inmsg, 0);
547
check(rc == 0, "Failed to receive message from control port.");
549
display_response((const char *)zmq_msg_data(inmsg), zmq_msg_size(inmsg));
557
if(outmsg) free(outmsg);
558
if(inmsg) free(inmsg);
562
int control_server(struct ServerRun *r, tns_value_t *res)
566
bstring prompt = NULL;
567
bstring control = NULL;
570
int rows = DB_counts(res, &cols);
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);
575
bstring server_name = DB_get_as(res, 0, 0, string);
576
bstring chroot = DB_get_as(res, 0, 1, string);
578
check(server_name != NULL && chroot != NULL,
579
"Somehow didn't get a good server_name and chroot.");
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));
588
socket = mqsocket(ZMQ_REQ);
589
check(socket != NULL, "Failed to create REQ socket.");
591
rc = zmq_connect(socket, bdata(control));
592
check(rc == 0, "Failed to connect to control port.");
594
rc = linenoise_runner(bdata(prompt), send_recv_control, socket);
603
if(prompt) bdestroy(prompt);
604
if(control) bdestroy(control);
605
if(socket) zmq_close(socket);
609
int Command_control(Command *cmd)
611
return exec_server_operations(cmd, control_server, "name, chroot");
614
static int run_command(bstring line, void *ignored)
616
bstring args = bformat("m2sh %s", bdata(line));
617
int rc = Command_run(args);
623
int Command_shell(Command *cmd)
625
return linenoise_runner("mongrel2> ", run_command, NULL);