1 diff -ruN asterisk-1.2.0-old/configs/res_mysql.conf.sample asterisk-1.2.0-new/configs/res_mysql.conf.sample
2 --- asterisk-1.2.0-old/configs/res_mysql.conf.sample 1970-01-01 01:00:00.000000000 +0100
3 +++ asterisk-1.2.0-new/configs/res_mysql.conf.sample 2004-12-03 15:33:44.000000000 +0100
6 +; Sample configuration for res_config_mysql.c
8 +; The value of dbhost may be either a hostname or an IP address.
9 +; If dbhost is commented out or the string "localhost", a connection
10 +; to the local host is assumed and dbsock is used instead of TCP/IP
11 +; to connect to the server.
19 +;dbsock = /tmp/mysql.sock
20 diff -ruN asterisk-1.2.0-old/res/res_config_mysql.c asterisk-1.2.0-new/res/res_config_mysql.c
21 --- asterisk-1.2.0-old/res/res_config_mysql.c 1970-01-01 01:00:00.000000000 +0100
22 +++ asterisk-1.2.0-new/res/res_config_mysql.c 2005-10-13 21:43:54.000000000 +0200
25 + * Asterisk -- A telephony toolkit for Linux.
27 + * Copyright (C) 1999-2005, Digium, Inc.
29 + * Mark Spencer <markster@digium.com> - Asterisk Author
30 + * Matthew Boehm <mboehm@cytelcom.com> - MySQL RealTime Driver Author
32 + * res_config_mysql.c <mysql plugin for RealTime configuration engine>
34 + * v2.0 - (10-07-05) - mutex_lock fixes (bug #4973, comment #0034602)
36 + * v1.9 - (08-19-05) - Added support to correctly honor the family database specified
37 + * in extconfig.conf (bug #4973)
39 + * v1.8 - (04-21-05) - Modified return values of update_mysql to better indicate
40 + * what really happened.
42 + * v1.7 - (01-28-05) - Fixed non-initialization of ast_category struct
43 + * in realtime_multi_mysql function which caused segfault.
45 + * v1.6 - (00-00-00) - Skipped to bring comments into sync with version number in CVS.
47 + * v1.5.1 - (01-26-05) - Added better(?) locking stuff
49 + * v1.5 - (01-26-05) - Brought up to date with new config.h changes (bug #3406)
50 + * - Added in extra locking provided by georg (bug #3248)
52 + * v1.4 - (12-02-04) - Added realtime_multi_mysql function
53 + * This function will return an ast_config with categories,
54 + * unlike standard realtime_mysql which only returns
55 + * a linked list of ast_variables
57 + * v1.3 - (12-01-04) - Added support other operators
58 + * Ex: =, !=, LIKE, NOT LIKE, RLIKE, etc...
60 + * v1.2 - (11-DD-04) - Added reload. Updated load and unload.
61 + * Code beautification (doc/CODING-GUIDELINES)
64 +#include <asterisk/channel.h>
65 +#include <asterisk/logger.h>
66 +#include <asterisk/config.h>
67 +#include <asterisk/module.h>
68 +#include <asterisk/lock.h>
69 +#include <asterisk/options.h>
70 +#include <asterisk/cli.h>
71 +#include <asterisk/utils.h>
75 +#include <mysql_version.h>
78 +static char *res_config_mysql_desc = "MySQL RealTime Configuration Driver";
80 +AST_MUTEX_DEFINE_STATIC(mysql_lock);
81 +#define RES_CONFIG_MYSQL_CONF "res_mysql.conf"
83 +static char dbhost[50];
84 +static char dbuser[50];
85 +static char dbpass[50];
86 +static char dbname[50];
87 +static char dbsock[50];
89 +static int connected;
90 +static time_t connect_time;
92 +static int parse_config(void);
93 +static int mysql_reconnect(const char *database);
94 +static int realtime_mysql_status(int fd, int argc, char **argv);
100 +static char cli_realtime_mysql_status_usage[] =
101 +"Usage: realtime mysql status\n"
102 +" Shows connection information for the MySQL RealTime driver\n";
104 +static struct ast_cli_entry cli_realtime_mysql_status = {
105 + { "realtime", "mysql", "status", NULL }, realtime_mysql_status,
106 + "Shows connection information for the MySQL RealTime driver", cli_realtime_mysql_status_usage, NULL };
108 +static struct ast_variable *realtime_mysql(const char *database, const char *table, va_list ap)
112 + MYSQL_FIELD *fields;
118 + const char *newparam, *newval;
119 + struct ast_variable *var=NULL, *prev=NULL;
122 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
126 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
127 + newparam = va_arg(ap, const char *);
128 + newval = va_arg(ap, const char *);
129 + if(!newparam || !newval) {
130 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
131 + mysql_close(&mysql);
135 + /* Create the first part of the query using the first parameter/value pairs we just extracted
136 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
138 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
140 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
141 + while((newparam = va_arg(ap, const char *))) {
142 + newval = va_arg(ap, const char *);
143 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
144 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
148 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
150 + /* We now have our complete statement; Lets connect to the server and execute it. */
151 + ast_mutex_lock(&mysql_lock);
152 + if(!mysql_reconnect(database)) {
153 + ast_mutex_unlock(&mysql_lock);
157 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
158 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
159 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
160 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
161 + ast_mutex_unlock(&mysql_lock);
165 + if((result = mysql_store_result(&mysql))) {
166 + numFields = mysql_num_fields(result);
167 + fields = mysql_fetch_fields(result);
169 + while((row = mysql_fetch_row(result))) {
170 + for(i = 0; i < numFields; i++) {
173 + chunk = strsep(&stringp, ";");
174 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
176 + prev->next = ast_variable_new(fields[i].name, chunk);
181 + prev = var = ast_variable_new(fields[i].name, chunk);
188 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
191 + ast_mutex_unlock(&mysql_lock);
192 + mysql_free_result(result);
197 +static struct ast_config *realtime_multi_mysql(const char *database, const char *table, va_list ap)
201 + MYSQL_FIELD *fields;
204 + const char *initfield = NULL;
208 + const char *newparam, *newval;
209 + struct ast_realloca ra;
210 + struct ast_variable *var=NULL;
211 + struct ast_config *cfg = NULL;
212 + struct ast_category *cat = NULL;
215 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
219 + memset(&ra, 0, sizeof(ra));
221 + cfg = ast_config_new();
223 + /* If I can't alloc memory at this point, why bother doing anything else? */
224 + ast_log(LOG_WARNING, "Out of memory!\n");
228 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
229 + newparam = va_arg(ap, const char *);
230 + newval = va_arg(ap, const char *);
231 + if(!newparam || !newval) {
232 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
233 + mysql_close(&mysql);
237 + initfield = ast_strdupa(newparam);
238 + if(initfield && (op = strchr(initfield, ' '))) {
242 + /* Create the first part of the query using the first parameter/value pairs we just extracted
243 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
245 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
247 + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, newval);
248 + while((newparam = va_arg(ap, const char *))) {
249 + newval = va_arg(ap, const char *);
250 + if(!strchr(newparam, ' ')) op = " ="; else op = "";
251 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, op, newval);
255 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
260 + ast_log(LOG_DEBUG, "MySQL RealTime: Retrieve SQL: %s\n", sql);
262 + /* We now have our complete statement; Lets connect to the server and execute it. */
263 + ast_mutex_lock(&mysql_lock);
264 + if(!mysql_reconnect(database)) {
265 + ast_mutex_unlock(&mysql_lock);
269 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
270 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
271 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
272 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
273 + ast_mutex_unlock(&mysql_lock);
277 + if((result = mysql_store_result(&mysql))) {
278 + numFields = mysql_num_fields(result);
279 + fields = mysql_fetch_fields(result);
281 + while((row = mysql_fetch_row(result))) {
283 + cat = ast_category_new("");
285 + ast_log(LOG_WARNING, "Out of memory!\n");
288 + for(i = 0; i < numFields; i++) {
291 + chunk = strsep(&stringp, ";");
292 + if(chunk && !ast_strlen_zero(ast_strip(chunk))) {
293 + if(initfield && !strcmp(initfield, fields[i].name)) {
294 + ast_category_rename(cat, chunk);
296 + var = ast_variable_new(fields[i].name, chunk);
297 + ast_variable_append(cat, var);
301 + ast_category_append(cfg, cat);
304 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find any rows in table %s.\n", table);
307 + ast_mutex_unlock(&mysql_lock);
308 + mysql_free_result(result);
313 +static int update_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
315 + my_ulonglong numrows;
317 + const char *newparam, *newval;
320 + ast_log(LOG_WARNING, "MySQL RealTime: No table specified.\n");
324 + /* Get the first parameter and first value in our list of passed paramater/value pairs */
325 + newparam = va_arg(ap, const char *);
326 + newval = va_arg(ap, const char *);
327 + if(!newparam || !newval) {
328 + ast_log(LOG_WARNING, "MySQL RealTime: Realtime retrieval requires at least 1 parameter and 1 value to search on.\n");
329 + mysql_close(&mysql);
333 + /* Create the first part of the query using the first parameter/value pairs we just extracted
334 + If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
336 + snprintf(sql, sizeof(sql), "UPDATE %s SET %s = '%s'", table, newparam, newval);
337 + while((newparam = va_arg(ap, const char *))) {
338 + newval = va_arg(ap, const char *);
339 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s = '%s'", newparam, newval);
342 + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s = '%s'", keyfield, lookup);
344 + ast_log(LOG_DEBUG,"MySQL RealTime: Update SQL: %s\n", sql);
346 + /* We now have our complete statement; Lets connect to the server and execute it. */
347 + ast_mutex_lock(&mysql_lock);
348 + if(!mysql_reconnect(database)) {
349 + ast_mutex_unlock(&mysql_lock);
353 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
354 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
355 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
356 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
357 + ast_mutex_unlock(&mysql_lock);
361 + numrows = mysql_affected_rows(&mysql);
362 + ast_mutex_unlock(&mysql_lock);
364 + ast_log(LOG_DEBUG,"MySQL RealTime: Updated %llu rows on table: %s\n", numrows, table);
366 + /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html
367 + * An integer greater than zero indicates the number of rows affected
368 + * Zero indicates that no records were updated
369 + * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.)
373 + return (int)numrows;
378 +static struct ast_config *config_mysql(const char *database, const char *table, const char *file, struct ast_config *cfg)
382 + my_ulonglong num_rows;
383 + struct ast_config *new;
384 + struct ast_variable *cur_v, *new_v;
385 + struct ast_category *cur_cat, *new_cat;
386 + char sql[250] = "";
387 + char last[80] = "";
388 + int cat_started = 0;
389 + int var_started = 0;
390 + int last_cat_metric = 0;
394 + if(!file || !strcmp(file, RES_CONFIG_MYSQL_CONF)) {
395 + ast_log(LOG_WARNING, "MySQL RealTime: Cannot configure myself.\n");
399 + snprintf(sql, sizeof(sql), "SELECT category, var_name, var_val, cat_metric FROM %s WHERE filename='%s' and commented=0 ORDER BY filename, cat_metric desc, var_metric asc, category, var_name, var_val, id", table, file);
401 + ast_log(LOG_DEBUG, "MySQL RealTime: Static SQL: %s\n", sql);
403 + /* We now have our complete statement; Lets connect to the server and execute it. */
404 + ast_mutex_lock(&mysql_lock);
405 + if(!mysql_reconnect(database)) {
406 + ast_mutex_unlock(&mysql_lock);
410 + if(mysql_real_query(&mysql, sql, strlen(sql))) {
411 + ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n");
412 + ast_log(LOG_DEBUG, "MySQL RealTime: Query: %s\n", sql);
413 + ast_log(LOG_DEBUG, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&mysql));
414 + ast_mutex_unlock(&mysql_lock);
418 + if((result = mysql_store_result(&mysql))) {
419 + num_rows = mysql_num_rows(result);
420 + ast_log(LOG_DEBUG, "MySQL RealTime: Found %llu rows.\n", num_rows);
422 + /* There might exist a better way to access the column names other than counting,
423 + but I believe that would require another loop that we don't need. */
425 + while((row = mysql_fetch_row(result))) {
426 + if(!strcmp(row[1], "#include")) {
427 + if (!ast_config_internal_load(row[2], cfg)) {
428 + mysql_free_result(result);
429 + ast_mutex_unlock(&mysql_lock);
435 + if(strcmp(last, row[0]) || last_cat_metric != atoi(row[3])) {
436 + cur_cat = ast_category_new(row[0]);
438 + ast_log(LOG_WARNING, "Out of memory!\n");
441 + strcpy(last, row[0]);
442 + last_cat_metric = atoi(row[3]);
443 + ast_category_append(cfg, cur_cat);
445 + new_v = ast_variable_new(row[1], row[2]);
446 + ast_variable_append(cur_cat, new_v);
449 + ast_log(LOG_WARNING, "MySQL RealTime: Could not find config '%s' in database.\n", file);
452 + mysql_free_result(result);
453 + ast_mutex_unlock(&mysql_lock);
458 +static struct ast_config_engine mysql_engine = {
460 + .load_func = config_mysql,
461 + .realtime_func = realtime_mysql,
462 + .realtime_multi_func = realtime_multi_mysql,
463 + .update_func = update_mysql
466 +int load_module (void)
470 + ast_mutex_lock(&mysql_lock);
472 + if(!mysql_reconnect(NULL)) {
473 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
474 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
477 + ast_config_engine_register(&mysql_engine);
478 + if(option_verbose) {
479 + ast_verbose("MySQL RealTime driver loaded.\n");
481 + ast_cli_register(&cli_realtime_mysql_status);
483 + ast_mutex_unlock(&mysql_lock);
488 +int unload_module (void)
490 + /* Aquire control before doing anything to the module itself. */
491 + ast_mutex_lock(&mysql_lock);
493 + mysql_close(&mysql);
494 + ast_cli_unregister(&cli_realtime_mysql_status);
495 + ast_config_engine_deregister(&mysql_engine);
496 + if(option_verbose) {
497 + ast_verbose("MySQL RealTime unloaded.\n");
500 + STANDARD_HANGUP_LOCALUSERS;
502 + /* Unlock so something else can destroy the lock. */
503 + ast_mutex_unlock(&mysql_lock);
510 + /* Aquire control before doing anything to the module itself. */
511 + ast_mutex_lock(&mysql_lock);
513 + mysql_close(&mysql);
517 + if(!mysql_reconnect(NULL)) {
518 + ast_log(LOG_WARNING, "MySQL RealTime: Couldn't establish connection. Check debug.\n");
519 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
522 + ast_verbose(VERBOSE_PREFIX_2 "MySQL RealTime reloaded.\n");
524 + /* Done reloading. Release lock so others can now use driver. */
525 + ast_mutex_unlock(&mysql_lock);
530 +int parse_config (void)
532 + struct ast_config *config;
535 + config = ast_config_load(RES_CONFIG_MYSQL_CONF);
538 + if(!(s=ast_variable_retrieve(config, "general", "dbuser"))) {
539 + ast_log(LOG_WARNING, "MySQL RealTime: No database user found, using 'asterisk' as default.\n");
540 + strncpy(dbuser, "asterisk", sizeof(dbuser) - 1);
542 + strncpy(dbuser, s, sizeof(dbuser) - 1);
545 + if(!(s=ast_variable_retrieve(config, "general", "dbpass"))) {
546 + ast_log(LOG_WARNING, "MySQL RealTime: No database password found, using 'asterisk' as default.\n");
547 + strncpy(dbpass, "asterisk", sizeof(dbpass) - 1);
549 + strncpy(dbpass, s, sizeof(dbpass) - 1);
552 + if(!(s=ast_variable_retrieve(config, "general", "dbhost"))) {
553 + ast_log(LOG_WARNING, "MySQL RealTime: No database host found, using localhost via socket.\n");
556 + strncpy(dbhost, s, sizeof(dbhost) - 1);
559 + if(!(s=ast_variable_retrieve(config, "general", "dbname"))) {
560 + ast_log(LOG_WARNING, "MySQL RealTime: No database name found, using 'asterisk' as default.\n");
561 + strncpy(dbname, "asterisk", sizeof(dbname) - 1);
563 + strncpy(dbname, s, sizeof(dbname) - 1);
566 + if(!(s=ast_variable_retrieve(config, "general", "dbport"))) {
567 + ast_log(LOG_WARNING, "MySQL RealTime: No database port found, using 3306 as default.\n");
573 + if(dbhost && !(s=ast_variable_retrieve(config, "general", "dbsock"))) {
574 + ast_log(LOG_WARNING, "MySQL RealTime: No database socket found, using '/tmp/mysql.sock' as default.\n");
575 + strncpy(dbsock, "/tmp/mysql.sock", sizeof(dbsock) - 1);
577 + strncpy(dbsock, s, sizeof(dbsock) - 1);
580 + ast_config_destroy(config);
583 + ast_log(LOG_DEBUG, "MySQL RealTime Host: %s\n", dbhost);
584 + ast_log(LOG_DEBUG, "MySQL RealTime Port: %i\n", dbport);
586 + ast_log(LOG_DEBUG, "MySQL RealTime Socket: %s\n", dbsock);
588 + ast_log(LOG_DEBUG, "MySQL RealTime User: %s\n", dbuser);
589 + ast_log(LOG_DEBUG, "MySQL RealTime Password: %s\n", dbpass);
594 +char *description (void)
596 + return res_config_mysql_desc;
601 + /* Try and get a lock. If unsuccessful, than that means another thread is using the mysql object. */
602 + if(ast_mutex_trylock(&mysql_lock)) {
603 + ast_log(LOG_DEBUG, "MySQL RealTime: Module usage count is 1.\n");
606 + ast_mutex_unlock(&mysql_lock);
612 + return ASTERISK_GPL_KEY;
615 +static int mysql_reconnect(const char *database)
617 + char my_database[50];
619 + if(!database || ast_strlen_zero(database))
620 + ast_copy_string(my_database, dbname, sizeof(my_database));
622 + ast_copy_string(my_database, database, sizeof(my_database));
624 + /* mutex lock should have been locked before calling this function. */
626 + if((!connected) && (dbhost || dbsock) && dbuser && dbpass && my_database) {
627 + if(!mysql_init(&mysql)) {
628 + ast_log(LOG_WARNING, "MySQL RealTime: Insufficient memory to allocate MySQL resource.\n");
632 + if(mysql_real_connect(&mysql, dbhost, dbuser, dbpass, my_database, dbport, dbsock, 0)) {
633 + ast_log(LOG_DEBUG, "MySQL RealTime: Successfully connected to database.\n");
635 + connect_time = time(NULL);
638 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to connect database server %s on %s. Check debug for more info.\n", dbname, dbhost);
639 + ast_log(LOG_DEBUG, "MySQL RealTime: Cannot Connect: %s\n", mysql_error(&mysql));
644 + if(mysql_ping(&mysql) != 0) {
646 + ast_log(LOG_ERROR, "MySQL RealTime: Failed to reconnect. Check debug for more info.\n");
647 + ast_log(LOG_DEBUG, "MySQL RealTime: Server Error: %s\n", mysql_error(&mysql));
653 + if(mysql_select_db(&mysql, my_database) != 0) {
654 + ast_log(LOG_WARNING, "MySQL RealTime: Unable to select database: %s. Still Connected.\n", my_database);
655 + ast_log(LOG_DEBUG, "MySQL RealTime: Database Select Failed: %s\n", mysql_error(&mysql));
659 + ast_log(LOG_DEBUG, "MySQL RealTime: Everything is fine.\n");
664 +static int realtime_mysql_status(int fd, int argc, char **argv)
666 + char status[256], status2[100] = "";
667 + int ctime = time(NULL) - connect_time;
669 + if(mysql_reconnect(NULL)) {
671 + snprintf(status, 255, "Connected to %s@%s, port %d", dbname, dbhost, dbport);
672 + } else if(dbsock) {
673 + snprintf(status, 255, "Connected to %s on socket file %s", dbname, dbsock);
675 + snprintf(status, 255, "Connected to %s@%s", dbname, dbhost);
678 + if(dbuser && *dbuser) {
679 + snprintf(status2, 99, " with username %s", dbuser);
682 + if (ctime > 31536000) {
683 + ast_cli(fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 31536000, (ctime % 31536000) / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
684 + } else if (ctime > 86400) {
685 + ast_cli(fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 86400, (ctime % 86400) / 3600, (ctime % 3600) / 60, ctime % 60);
686 + } else if (ctime > 3600) {
687 + ast_cli(fd, "%s%s for %d hours, %d minutes, %d seconds.\n", status, status2, ctime / 3600, (ctime % 3600) / 60, ctime % 60);
688 + } else if (ctime > 60) {
689 + ast_cli(fd, "%s%s for %d minutes, %d seconds.\n", status, status2, ctime / 60, ctime % 60);
691 + ast_cli(fd, "%s%s for %d seconds.\n", status, status2, ctime);
694 + return RESULT_SUCCESS;
696 + return RESULT_FAILURE;