diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 07edcec626c74d4255234572e45cc031cfb44063..5e3e439fda7c33467450ebc9af37ef635444572e 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -1784,6 +1784,12 @@ enum ast_sip_scheduler_task_flags { */ AST_SIP_SCHED_TASK_VARIABLE = (1 << 0), + /*! + * Run just once. + * Return values are ignored. + */ + AST_SIP_SCHED_TASK_ONESHOT = (1 << 6), + /*! * The task data is not an AO2 object. */ @@ -1905,6 +1911,26 @@ int ast_sip_sched_task_cancel_by_name(const char *name); int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end); +/*! + * \brief Gets the queued, last start, last_end, time left, interval, next run + * \since 16.15.0 + * \since 18.1.0 + * + * \param schtd The task structure pointer + * \param[out] when_queued Pointer to a timeval structure to contain the time when queued + * \param[out] last_start Pointer to a timeval structure to contain the time when last started + * \param[out] last_end Pointer to a timeval structure to contain the time when last ended + * \param[out] interval Pointer to an int to contain the interval in ms + * \param[out] time_left Pointer to an int to contain the ms left to the next run + * \param[out] last_end Pointer to a timeval structure to contain the next run time + * \retval 0 Success + * \retval -1 Failure + * \note Any of the pointers can be NULL if you don't need them. + */ +int ast_sip_sched_task_get_times2(struct ast_sip_sched_task *schtd, + struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end, + int *interval, int *time_left, struct timeval *next_start); + /*! * \brief Gets the last start and end times of the task by name * \since 13.9.0 @@ -1920,6 +1946,26 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, int ast_sip_sched_task_get_times_by_name(const char *name, struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end); +/*! + * \brief Gets the queued, last start, last_end, time left, interval, next run by task name + * \since 16.15.0 + * \since 18.1.0 + * + * \param name The task name + * \param[out] when_queued Pointer to a timeval structure to contain the time when queued + * \param[out] last_start Pointer to a timeval structure to contain the time when last started + * \param[out] last_end Pointer to a timeval structure to contain the time when last ended + * \param[out] interval Pointer to an int to contain the interval in ms + * \param[out] time_left Pointer to an int to contain the ms left to the next run + * \param[out] last_end Pointer to a timeval structure to contain the next run time + * \retval 0 Success + * \retval -1 Failure + * \note Any of the pointers can be NULL if you don't need them. + */ +int ast_sip_sched_task_get_times_by_name2(const char *name, + struct timeval *when_queued, struct timeval *last_start, struct timeval *last_end, + int *interval, int *time_left, struct timeval *next_start); + /*! * \brief Gets the number of milliseconds until the next invocation * \since 13.9.0 diff --git a/res/res_pjsip/pjsip_scheduler.c b/res/res_pjsip/pjsip_scheduler.c index bbf666fd7b5671c8e901ddb99654a02c62b40c59..ac53379f240d8a7512fcb2b2859ad781320613da 100644 --- a/res/res_pjsip/pjsip_scheduler.c +++ b/res/res_pjsip/pjsip_scheduler.c @@ -30,6 +30,8 @@ #include "asterisk/res_pjsip_cli.h" #include "asterisk/taskprocessor.h" +#include <regex.h> + #define TASK_BUCKETS 53 static struct ast_sched_context *scheduler_context; @@ -105,7 +107,7 @@ static int run_task(void *data) * Don't restart if the task returned <= 0 or if the interval * was set to 0 while the task was running */ - if (res <= 0 || !schtd->interval) { + if ((schtd->flags & AST_SIP_SCHED_TASK_ONESHOT) || res <= 0 || !schtd->interval) { schtd->interval = 0; ao2_unlock(schtd); ao2_unlink(tasks, schtd); @@ -232,9 +234,9 @@ int ast_sip_sched_task_cancel_by_name(const char *name) return res; } - -int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, - struct timeval *queued, struct timeval *last_start, struct timeval *last_end) +int ast_sip_sched_task_get_times2(struct ast_sip_sched_task *schtd, + struct timeval *queued, struct timeval *last_start, struct timeval *last_end, + int *interval, int *time_left, struct timeval *next_start) { ao2_lock(schtd); if (queued) { @@ -246,13 +248,63 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, if (last_end) { memcpy(last_end, &schtd->last_end, sizeof(struct timeval)); } + + if (interval) { + *interval = schtd->interval; + } + + if (time_left || next_start) { + int delay; + struct timeval since_when; + struct timeval now; + struct timeval next; + + if (schtd->interval) { + delay = schtd->interval; + now = ast_tvnow(); + + if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) { + since_when = schtd->is_running ? now : schtd->last_end; + } else { + since_when = schtd->last_start.tv_sec ? schtd->last_start : schtd->when_queued; + } + + delay -= ast_tvdiff_ms(now, since_when); + + delay = delay < 0 ? 0 : delay; + + + if (time_left) { + *time_left = delay; + } + + if (next_start) { + next = ast_tvadd(now, ast_tv(delay / 1000, (delay % 1000) * 1000)); + memcpy(next_start, &next, sizeof(struct timeval)); + } + } else { + if (time_left) { + *time_left = -1; + } + } + + } + ao2_unlock(schtd); return 0; } -int ast_sip_sched_task_get_times_by_name(const char *name, + +int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd, struct timeval *queued, struct timeval *last_start, struct timeval *last_end) +{ + return ast_sip_sched_task_get_times2(schtd, queued, last_start, last_end, NULL, NULL, NULL); +} + +int ast_sip_sched_task_get_times_by_name2(const char *name, + struct timeval *queued, struct timeval *last_start, struct timeval *last_end, + int *interval, int *time_left, struct timeval *next_start) { int res; struct ast_sip_sched_task *schtd; @@ -266,11 +318,19 @@ int ast_sip_sched_task_get_times_by_name(const char *name, return -1; } - res = ast_sip_sched_task_get_times(schtd, queued, last_start, last_end); + res = ast_sip_sched_task_get_times2(schtd, queued, last_start, last_end, interval, time_left, + next_start); ao2_ref(schtd, -1); return res; } +int ast_sip_sched_task_get_times_by_name(const char *name, + struct timeval *queued, struct timeval *last_start, struct timeval *last_end) +{ + return ast_sip_sched_task_get_times_by_name2(name, queued, last_start, last_end, + NULL, NULL, NULL); +} + int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen) { if (maxlen <= 0) { @@ -287,29 +347,8 @@ int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, si int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd) { int delay; - struct timeval since_when; - struct timeval now; - - ao2_lock(schtd); - - if (schtd->interval) { - delay = schtd->interval; - now = ast_tvnow(); - - if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) { - since_when = schtd->is_running ? now : schtd->last_end; - } else { - since_when = schtd->last_start.tv_sec ? schtd->last_start : schtd->when_queued; - } - delay -= ast_tvdiff_ms(now, since_when); - - delay = delay < 0 ? 0 : delay; - } else { - delay = -1; - } - - ao2_unlock(schtd); + ast_sip_sched_task_get_times2(schtd, NULL, NULL, NULL, NULL, &delay, NULL); return delay; } @@ -396,6 +435,7 @@ struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *seria schtd->task = sip_task; schtd->interval = interval; schtd->flags = flags; + schtd->last_start = ast_tv(0, 0); if (!ast_strlen_zero(name)) { strcpy(schtd->name, name); /* Safe */ } else { @@ -445,60 +485,75 @@ struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *seria #undef ID_LEN } +#define TIME_FORMAT "%a %T" + static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_iterator iter; struct ao2_container *sorted_tasks; struct ast_sip_sched_task *schtd; - const char *log_format; struct ast_tm tm; + char times_run[16]; char queued[32]; char last_start[32]; char next_start[32]; - int datelen; struct timeval now; - static const char separator[] = "============================================="; + int using_regex = 0; + regex_t regex; switch (cmd) { case CLI_INIT: e->command = "pjsip show scheduled_tasks"; - e->usage = "Usage: pjsip show scheduled_tasks\n" - " Show all scheduled tasks\n"; + e->usage = "Usage: pjsip show scheduled_tasks [ like <pattern> ]\n" + " Show scheduled pjsip tasks\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc != 3) { + if (a->argc != 3 && a->argc != 5) { return CLI_SHOWUSAGE; } + if (a->argc == 5) { + int regrc; + if (!strcasecmp(a->argv[3], "like") == 0) { + return CLI_SHOWUSAGE; + } + regrc = regcomp(®ex, a->argv[4], REG_EXTENDED | REG_ICASE | REG_NOSUB); + if (regrc) { + char err[256]; + regerror(regrc, ®ex, err, 256); + ast_cli(a->fd, "PJSIP Scheduled Tasks: Error: %s\n", err); + return CLI_FAILURE; + } + using_regex = 1; + } + /* Get a sorted snapshot of the scheduled tasks */ sorted_tasks = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sip_sched_task_sort_fn, NULL); if (!sorted_tasks) { - return CLI_SUCCESS; + ast_cli(a->fd, "PJSIP Scheduled Tasks: Unable to allocate temporary container\n"); + return CLI_FAILURE; } if (ao2_container_dup(sorted_tasks, tasks, 0)) { ao2_ref(sorted_tasks, -1); - return CLI_SUCCESS; + ast_cli(a->fd, "PJSIP Scheduled Tasks: Unable to sort temporary container\n"); + return CLI_FAILURE; } now = ast_tvnow(); - log_format = ast_logger_get_dateformat(); ast_localtime(&now, &tm, NULL); - datelen = ast_strftime(queued, sizeof(queued), log_format, &tm); ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n"); - ast_cli(a->fd, "%1$-45s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n", - "Task Name", "Interval", "Times Run", "State", - datelen, "Queued", "Last Started", "Next Start", "( secs)"); - - ast_cli(a->fd, "%1$-45.45s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n", - separator, separator, separator, separator, - datelen, separator, separator, datelen + 8, separator); + ast_cli(a->fd, + "<Task Name....................................> <Interval> <Times Run> <State>" + " <Queued....> <Last Start> <Next Start.....secs>\n" + "==============================================================================" + "===================================================\n"); iter = ao2_iterator_init(sorted_tasks, AO2_ITERATOR_UNLINK); for (; (schtd = ao2_iterator_next(&iter)); ao2_ref(schtd, -1)) { @@ -507,6 +562,11 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg ao2_lock(schtd); + if (using_regex && regexec(®ex, schtd->name, 0, NULL, 0) == REG_NOMATCH) { + ao2_unlock(schtd); + continue; + } + next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000; if (next_run_sec < 0) { /* Scheduled task is now canceled */ @@ -516,37 +576,39 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg next = ast_tvadd(now, ast_tv(next_run_sec, 0)); ast_localtime(&schtd->when_queued, &tm, NULL); - ast_strftime(queued, sizeof(queued), log_format, &tm); + ast_strftime(queued, sizeof(queued), TIME_FORMAT, &tm); - if (ast_tvzero(schtd->last_start)) { - strcpy(last_start, "not yet started"); - } else { - ast_localtime(&schtd->last_start, &tm, NULL); - ast_strftime(last_start, sizeof(last_start), log_format, &tm); - } + ast_localtime(&schtd->last_start, &tm, NULL); + ast_strftime(last_start, sizeof(last_start), TIME_FORMAT, &tm); ast_localtime(&next, &tm, NULL); - ast_strftime(next_start, sizeof(next_start), log_format, &tm); + ast_strftime(next_start, sizeof(next_start), TIME_FORMAT, &tm); + + sprintf(times_run, "%d", schtd->run_count); - ast_cli(a->fd, "%1$-46.46s%2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n", + ast_cli(a->fd, "%-46.46s %9d %9s %-5s %-12s %-12s %-12s %8d\n", schtd->name, - schtd->interval / 1000.0, - schtd->run_count, + schtd->interval / 1000, + schtd->flags & AST_SIP_SCHED_TASK_ONESHOT ? "oneshot" : times_run, schtd->is_running ? "run" : "wait", - datelen, queued, last_start, + queued, + (ast_tvzero(schtd->last_start) || (schtd->flags & AST_SIP_SCHED_TASK_ONESHOT) ? "" : last_start), next_start, next_run_sec); ao2_unlock(schtd); } + if (using_regex) { + regfree(®ex); + } ao2_iterator_destroy(&iter); + ast_cli(a->fd, "\nTotal Scheduled Tasks: %d\n\n", ao2_container_count(sorted_tasks)); ao2_ref(sorted_tasks, -1); - ast_cli(a->fd, "\n"); return CLI_SUCCESS; } static struct ast_cli_entry cli_commands[] = { - AST_CLI_DEFINE(cli_show_tasks, "Show all scheduled tasks"), + AST_CLI_DEFINE(cli_show_tasks, "Show pjsip scheduled tasks"), }; int ast_sip_initialize_scheduler(void)