2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2012, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
20 /* Doxygenified Copyright Header */
22 * \mainpage Asterisk -- The Open Source Telephony Project
24 * \par Developer Documentation for Asterisk
26 * This is the main developer documentation for Asterisk. It is
27 * generated by running "make progdocs" from the Asterisk source tree.
29 * In addition to the information available on the Asterisk source code,
30 * please see the appendices for information on coding guidelines,
31 * release management, commit policies, and more.
33 * \arg \ref AsteriskArchitecture
35 * \par Additional documentation
38 * \arg \ref ConfigFiles
40 * \section copyright Copyright and Author
42 * Copyright (C) 1999 - 2012, Digium, Inc.
43 * Asterisk is a <a href="http://www.digium.com/en/company/view-policy.php?id=Trademark-Policy">registered trademark</a>
44 * of <a href="http://www.digium.com">Digium, Inc</a>.
46 * \author Mark Spencer <markster@digium.com>
47 * Also see \ref AstCREDITS
49 * See http://www.asterisk.org for more information about
50 * the Asterisk project. Please do not directly contact
51 * any of the maintainers of this project for assistance;
52 * the project provides a web site, mailing lists, and IRC
53 * channels for your use.
57 \brief Top level source file for Asterisk - the Open Source PBX. Implementation
58 of PBX core functions and CLI interface.
63 <support_level>core</support_level>
68 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
70 #include "asterisk/_private.h"
72 #undef sched_setscheduler
81 #include <sys/resource.h>
85 #if defined(HAVE_SYSINFO)
86 #include <sys/sysinfo.h>
87 #elif defined(HAVE_SYSCTL)
88 #include <sys/param.h>
89 #include <sys/sysctl.h>
90 #if !defined(__OpenBSD__)
91 #include <sys/vmmeter.h>
92 #if defined(__FreeBSD__)
93 #include <vm/vm_param.h>
96 #if defined(HAVE_SWAPCTL)
101 #include <histedit.h>
104 int daemon(int, int); /* defined in libresolv of all places */
105 #include <sys/loadavg.h>
109 #include <sys/prctl.h>
111 #include <sys/capability.h>
112 #endif /* HAVE_CAP */
115 #include "asterisk/paths.h" /* we define here the variables so better agree on the prototype */
116 #include "asterisk/network.h"
117 #include "asterisk/cli.h"
118 #include "asterisk/channel.h"
119 #include "asterisk/translate.h"
120 #include "asterisk/features.h"
121 #include "asterisk/acl.h"
122 #include "asterisk/ulaw.h"
123 #include "asterisk/alaw.h"
124 #include "asterisk/callerid.h"
125 #include "asterisk/image.h"
126 #include "asterisk/tdd.h"
127 #include "asterisk/term.h"
128 #include "asterisk/manager.h"
129 #include "asterisk/cdr.h"
130 #include "asterisk/cel.h"
131 #include "asterisk/pbx.h"
132 #include "asterisk/enum.h"
133 #include "asterisk/http.h"
134 #include "asterisk/udptl.h"
135 #include "asterisk/app.h"
136 #include "asterisk/lock.h"
137 #include "asterisk/utils.h"
138 #include "asterisk/file.h"
139 #include "asterisk/io.h"
140 #include "editline/histedit.h"
141 #include "asterisk/config.h"
142 #include "asterisk/ast_version.h"
143 #include "asterisk/linkedlists.h"
144 #include "asterisk/devicestate.h"
145 #include "asterisk/presencestate.h"
146 #include "asterisk/module.h"
147 #include "asterisk/dsp.h"
148 #include "asterisk/buildinfo.h"
149 #include "asterisk/xmldoc.h"
150 #include "asterisk/poll-compat.h"
151 #include "asterisk/ccss.h"
152 #include "asterisk/test.h"
153 #include "asterisk/rtp_engine.h"
154 #include "asterisk/format.h"
155 #include "asterisk/aoc.h"
157 #include "../defaults.h"
163 #define AF_LOCAL AF_UNIX
164 #define PF_LOCAL PF_UNIX
167 #define AST_MAX_CONNECTS 128
170 /*! Default minimum DTMF digit length - 80ms */
171 #define AST_MIN_DTMF_DURATION 80
174 /*! \brief Welcome message when starting a CLI interface */
175 #define WELCOME_MESSAGE \
176 ast_verbose("Asterisk %s, Copyright (C) 1999 - 2012 Digium, Inc. and others.\n" \
177 "Created by Mark Spencer <markster@digium.com>\n" \
178 "Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
179 "This is free software, with components licensed under the GNU General Public\n" \
180 "License version 2 and other licenses; you are welcome to redistribute it under\n" \
181 "certain conditions. Type 'core show license' for details.\n" \
182 "=========================================================================\n", ast_get_version()) \
184 /*! \defgroup main_options Main Configuration Options
185 * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
186 * \arg \ref Config_ast "asterisk.conf"
187 * \note Some of them can be changed in the CLI
191 struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
192 struct ast_flags ast_compat = { 0 };
194 int option_verbose; /*!< Verbosity level */
195 int option_debug; /*!< Debug level */
196 double option_maxload; /*!< Max load avg on system */
197 int option_maxcalls; /*!< Max number of active calls */
198 int option_maxfiles; /*!< Max number of open file handles (files, sockets) */
199 unsigned int option_dtmfminduration; /*!< Minimum duration of DTMF. */
200 #if defined(HAVE_SYSINFO)
201 long option_minmemfree; /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
206 struct ast_eid ast_eid_default;
208 /* XXX tmpdir is a subdir of the spool directory, and no way to remap it */
209 char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
211 static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
212 static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
215 int fd; /*!< File descriptor */
216 int p[2]; /*!< Pipe */
217 pthread_t t; /*!< Thread of handler */
218 int mute; /*!< Is the console muted for logs */
219 int uid; /*!< Remote user ID. */
220 int gid; /*!< Remote group ID. */
221 int levels[NUMLOGLEVELS]; /*!< Which log levels are enabled for the console */
226 AST_RWLIST_ENTRY(ast_atexit) list;
229 static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);
231 struct timeval ast_startuptime;
232 struct timeval ast_lastreloadtime;
234 static History *el_hist;
236 static char *remotehostname;
238 struct console consoles[AST_MAX_CONNECTS];
240 char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
242 static int ast_el_add_history(char *);
243 static int ast_el_read_history(char *);
244 static int ast_el_write_history(char *);
247 char config_dir[PATH_MAX];
248 char module_dir[PATH_MAX];
249 char spool_dir[PATH_MAX];
250 char monitor_dir[PATH_MAX];
251 char var_dir[PATH_MAX];
252 char data_dir[PATH_MAX];
253 char log_dir[PATH_MAX];
254 char agi_dir[PATH_MAX];
255 char run_dir[PATH_MAX];
256 char key_dir[PATH_MAX];
258 char config_file[PATH_MAX];
259 char db_path[PATH_MAX];
260 char sbin_dir[PATH_MAX];
261 char pid_path[PATH_MAX];
262 char socket_path[PATH_MAX];
263 char run_user[PATH_MAX];
264 char run_group[PATH_MAX];
265 char system_name[128];
268 static struct _cfg_paths cfg_paths;
270 const char *ast_config_AST_CONFIG_DIR = cfg_paths.config_dir;
271 const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
272 const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
273 const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
274 const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
275 const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
276 const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
277 const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
278 const char *ast_config_AST_AGI_DIR = cfg_paths.agi_dir;
279 const char *ast_config_AST_KEY_DIR = cfg_paths.key_dir;
280 const char *ast_config_AST_RUN_DIR = cfg_paths.run_dir;
281 const char *ast_config_AST_SBIN_DIR = cfg_paths.sbin_dir;
283 const char *ast_config_AST_DB = cfg_paths.db_path;
284 const char *ast_config_AST_PID = cfg_paths.pid_path;
285 const char *ast_config_AST_SOCKET = cfg_paths.socket_path;
286 const char *ast_config_AST_RUN_USER = cfg_paths.run_user;
287 const char *ast_config_AST_RUN_GROUP = cfg_paths.run_group;
288 const char *ast_config_AST_SYSTEM_NAME = cfg_paths.system_name;
290 static char ast_config_AST_CTL_PERMISSIONS[PATH_MAX];
291 static char ast_config_AST_CTL_OWNER[PATH_MAX] = "\0";
292 static char ast_config_AST_CTL_GROUP[PATH_MAX] = "\0";
293 static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
295 extern unsigned int ast_FD_SETSIZE;
297 static char *_argv[256];
299 NOT_SHUTTING_DOWN = -2,
301 /* Valid values for quit_handler niceness below: */
307 static shutdown_nice_t shuttingdown = NOT_SHUTTING_DOWN;
308 static int restartnow;
309 static pthread_t consolethread = AST_PTHREADT_NULL;
310 static pthread_t mon_sig_flags;
311 static int canary_pid = 0;
312 static char canary_filename[128];
314 static char randompool[256];
316 static int sig_alert_pipe[2] = { -1, -1 };
318 unsigned int need_reload:1;
319 unsigned int need_quit:1;
320 unsigned int need_quit_handler:1;
323 #if !defined(LOW_MEMORY)
324 struct file_version {
325 AST_RWLIST_ENTRY(file_version) list;
330 static AST_RWLIST_HEAD_STATIC(file_versions, file_version);
332 void ast_register_file_version(const char *file, const char *version)
334 struct file_version *new;
336 size_t version_length;
338 work = ast_strdupa(version);
339 work = ast_strip(ast_strip_quoted(work, "$", "$"));
340 version_length = strlen(work) + 1;
342 if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
346 new->version = (char *) new + sizeof(*new);
347 memcpy(new->version, work, version_length);
348 AST_RWLIST_WRLOCK(&file_versions);
349 AST_RWLIST_INSERT_HEAD(&file_versions, new, list);
350 AST_RWLIST_UNLOCK(&file_versions);
353 void ast_unregister_file_version(const char *file)
355 struct file_version *find;
357 AST_RWLIST_WRLOCK(&file_versions);
358 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
359 if (!strcasecmp(find->file, file)) {
360 AST_RWLIST_REMOVE_CURRENT(list);
364 AST_RWLIST_TRAVERSE_SAFE_END;
365 AST_RWLIST_UNLOCK(&file_versions);
371 char *ast_complete_source_filename(const char *partial, int n)
373 struct file_version *find;
374 size_t len = strlen(partial);
378 AST_RWLIST_RDLOCK(&file_versions);
379 AST_RWLIST_TRAVERSE(&file_versions, find, list) {
380 if (!strncasecmp(find->file, partial, len) && ++count > n) {
381 res = ast_strdup(find->file);
385 AST_RWLIST_UNLOCK(&file_versions);
389 /*! \brief Find version for given module name */
390 const char *ast_file_version_find(const char *file)
392 struct file_version *iterator;
394 AST_RWLIST_WRLOCK(&file_versions);
395 AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
396 if (!strcasecmp(iterator->file, file))
399 AST_RWLIST_UNLOCK(&file_versions);
401 return iterator->version;
407 struct thread_list_t {
408 AST_RWLIST_ENTRY(thread_list_t) list;
414 static AST_RWLIST_HEAD_STATIC(thread_list, thread_list_t);
416 void ast_register_thread(char *name)
418 struct thread_list_t *new = ast_calloc(1, sizeof(*new));
422 new->id = pthread_self();
423 new->lwp = ast_get_tid();
424 new->name = name; /* steal the allocated memory for the thread name */
425 AST_RWLIST_WRLOCK(&thread_list);
426 AST_RWLIST_INSERT_HEAD(&thread_list, new, list);
427 AST_RWLIST_UNLOCK(&thread_list);
430 void ast_unregister_thread(void *id)
432 struct thread_list_t *x;
434 AST_RWLIST_WRLOCK(&thread_list);
435 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&thread_list, x, list) {
436 if ((void *) x->id == id) {
437 AST_RWLIST_REMOVE_CURRENT(list);
441 AST_RWLIST_TRAVERSE_SAFE_END;
442 AST_RWLIST_UNLOCK(&thread_list);
449 /*! \brief Give an overview of core settings */
450 static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
458 e->command = "core show settings";
459 e->usage = "Usage: core show settings\n"
460 " Show core misc settings";
466 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
468 ast_cli(a->fd, "\nPBX Core settings\n");
469 ast_cli(a->fd, "-----------------\n");
470 ast_cli(a->fd, " Version: %s\n", ast_get_version());
471 ast_cli(a->fd, " Build Options: %s\n", S_OR(AST_BUILDOPTS, "(none)"));
473 ast_cli(a->fd, " Maximum calls: %d (Current %d)\n", option_maxcalls, ast_active_channels());
475 ast_cli(a->fd, " Maximum calls: Not set\n");
477 ast_cli(a->fd, " Maximum open file handles: %d\n", option_maxfiles);
479 ast_cli(a->fd, " Maximum open file handles: Not set\n");
480 ast_cli(a->fd, " Verbosity: %d\n", option_verbose);
481 ast_cli(a->fd, " Debug level: %d\n", option_debug);
482 ast_cli(a->fd, " Maximum load average: %lf\n", option_maxload);
483 #if defined(HAVE_SYSINFO)
484 ast_cli(a->fd, " Minimum free memory: %ld MB\n", option_minmemfree);
486 if (ast_localtime(&ast_startuptime, &tm, NULL)) {
487 ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
488 ast_cli(a->fd, " Startup time: %s\n", buf);
490 if (ast_localtime(&ast_lastreloadtime, &tm, NULL)) {
491 ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
492 ast_cli(a->fd, " Last reload time: %s\n", buf);
494 ast_cli(a->fd, " System: %s/%s built by %s on %s %s\n", ast_build_os, ast_build_kernel, ast_build_user, ast_build_machine, ast_build_date);
495 ast_cli(a->fd, " System name: %s\n", ast_config_AST_SYSTEM_NAME);
496 ast_cli(a->fd, " Entity ID: %s\n", eid_str);
497 ast_cli(a->fd, " Default language: %s\n", defaultlanguage);
498 ast_cli(a->fd, " Language prefix: %s\n", ast_language_is_prefix ? "Enabled" : "Disabled");
499 ast_cli(a->fd, " User name and group: %s/%s\n", ast_config_AST_RUN_USER, ast_config_AST_RUN_GROUP);
500 ast_cli(a->fd, " Executable includes: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) ? "Enabled" : "Disabled");
501 ast_cli(a->fd, " Transcode via SLIN: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN) ? "Enabled" : "Disabled");
502 ast_cli(a->fd, " Internal timing: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
503 ast_cli(a->fd, " Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) ? "Enabled" : "Disabled");
504 ast_cli(a->fd, " Generic PLC: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_GENERIC_PLC) ? "Enabled" : "Disabled");
505 ast_cli(a->fd, " Min DTMF duration:: %u\n", option_dtmfminduration);
507 ast_cli(a->fd, "\n* Subsystems\n");
508 ast_cli(a->fd, " -------------\n");
509 ast_cli(a->fd, " Manager (AMI): %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
510 ast_cli(a->fd, " Web Manager (AMI/HTTP): %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
511 ast_cli(a->fd, " Call data records: %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
512 ast_cli(a->fd, " Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
514 /*! \todo we could check musiconhold, voicemail, smdi, adsi, queues */
516 ast_cli(a->fd, "\n* Directories\n");
517 ast_cli(a->fd, " -------------\n");
518 ast_cli(a->fd, " Configuration file: %s\n", ast_config_AST_CONFIG_FILE);
519 ast_cli(a->fd, " Configuration directory: %s\n", ast_config_AST_CONFIG_DIR);
520 ast_cli(a->fd, " Module directory: %s\n", ast_config_AST_MODULE_DIR);
521 ast_cli(a->fd, " Spool directory: %s\n", ast_config_AST_SPOOL_DIR);
522 ast_cli(a->fd, " Log directory: %s\n", ast_config_AST_LOG_DIR);
523 ast_cli(a->fd, " Run/Sockets directory: %s\n", ast_config_AST_RUN_DIR);
524 ast_cli(a->fd, " PID file: %s\n", ast_config_AST_PID);
525 ast_cli(a->fd, " VarLib directory: %s\n", ast_config_AST_VAR_DIR);
526 ast_cli(a->fd, " Data directory: %s\n", ast_config_AST_DATA_DIR);
527 ast_cli(a->fd, " ASTDB: %s\n", ast_config_AST_DB);
528 ast_cli(a->fd, " IAX2 Keys directory: %s\n", ast_config_AST_KEY_DIR);
529 ast_cli(a->fd, " AGI Scripts directory: %s\n", ast_config_AST_AGI_DIR);
530 ast_cli(a->fd, "\n\n");
534 static char *handle_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
537 struct thread_list_t *cur;
540 e->command = "core show threads";
542 "Usage: core show threads\n"
543 " List threads currently active in the system.\n";
549 AST_RWLIST_RDLOCK(&thread_list);
550 AST_RWLIST_TRAVERSE(&thread_list, cur, list) {
551 ast_cli(a->fd, "%p %d %s\n", (void *)cur->id, cur->lwp, cur->name);
554 AST_RWLIST_UNLOCK(&thread_list);
555 ast_cli(a->fd, "%d threads listed.\n", count);
559 #if defined (HAVE_SYSCTL) && defined(HAVE_SWAPCTL)
561 * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org>
562 * to be based on the new swapctl(2) system call.
564 static int swapmode(int *used, int *total)
566 struct swapent *swdev;
567 int nswap, rnswap, i;
569 nswap = swapctl(SWAP_NSWAP, 0, 0);
573 swdev = ast_calloc(nswap, sizeof(*swdev));
577 rnswap = swapctl(SWAP_STATS, swdev, nswap);
583 /* if rnswap != nswap, then what? */
585 /* Total things up */
587 for (i = 0; i < nswap; i++) {
588 if (swdev[i].se_flags & SWF_ENABLE) {
589 *used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
590 *total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
596 #elif defined(HAVE_SYSCTL) && !defined(HAVE_SYSINFO)
597 static int swapmode(int *used, int *total)
604 #if defined(HAVE_SYSINFO) || defined(HAVE_SYSCTL)
605 /*! \brief Give an overview of system statistics */
606 static char *handle_show_sysinfo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
608 uint64_t physmem, freeram;
609 uint64_t freeswap = 0;
613 #if defined(HAVE_SYSINFO)
614 struct sysinfo sys_info;
616 uptime = sys_info.uptime / 3600;
617 physmem = sys_info.totalram * sys_info.mem_unit;
618 freeram = (sys_info.freeram * sys_info.mem_unit) / 1024;
619 totalswap = (sys_info.totalswap * sys_info.mem_unit) / 1024;
620 freeswap = (sys_info.freeswap * sys_info.mem_unit) / 1024;
621 nprocs = sys_info.procs;
622 #elif defined(HAVE_SYSCTL)
623 static int pageshift;
624 struct vmtotal vmtotal;
625 struct timeval boottime;
627 int mib[2], pagesize, usedswap = 0;
629 /* calculate the uptime by looking at boottime */
632 mib[1] = KERN_BOOTTIME;
633 len = sizeof(boottime);
634 if (sysctl(mib, 2, &boottime, &len, NULL, 0) != -1) {
635 uptime = now - boottime.tv_sec;
637 uptime = uptime/3600;
638 /* grab total physical memory */
640 #if defined(HW_PHYSMEM64)
641 mib[1] = HW_PHYSMEM64;
645 len = sizeof(physmem);
646 sysctl(mib, 2, &physmem, &len, NULL, 0);
648 pagesize = getpagesize();
650 while (pagesize > 1) {
655 /* we only need the amount of log(2)1024 for our conversion */
661 len = sizeof(vmtotal);
662 sysctl(mib, 2, &vmtotal, &len, NULL, 0);
663 freeram = (vmtotal.t_free << pageshift);
664 /* generate swap usage and totals */
665 swapmode(&usedswap, &totalswap);
666 freeswap = (totalswap - usedswap);
667 /* grab number of processes */
668 #if defined(__OpenBSD__)
670 mib[1] = KERN_NPROCS;
671 len = sizeof(nprocs);
672 sysctl(mib, 2, &nprocs, &len, NULL, 0);
678 e->command = "core show sysinfo";
680 "Usage: core show sysinfo\n"
681 " List current system information.\n";
687 ast_cli(a->fd, "\nSystem Statistics\n");
688 ast_cli(a->fd, "-----------------\n");
689 ast_cli(a->fd, " System Uptime: %lu hours\n", uptime);
690 ast_cli(a->fd, " Total RAM: %" PRIu64 " KiB\n", physmem / 1024);
691 ast_cli(a->fd, " Free RAM: %" PRIu64 " KiB\n", freeram);
692 #if defined(HAVE_SYSINFO)
693 ast_cli(a->fd, " Buffer RAM: %" PRIu64 " KiB\n", ((uint64_t) sys_info.bufferram * sys_info.mem_unit) / 1024);
695 #if defined (HAVE_SYSCTL) || defined(HAVE_SWAPCTL)
696 ast_cli(a->fd, " Total Swap Space: %u KiB\n", totalswap);
697 ast_cli(a->fd, " Free Swap Space: %" PRIu64 " KiB\n\n", freeswap);
699 ast_cli(a->fd, " Number of Processes: %d \n\n", nprocs);
704 struct profile_entry {
706 uint64_t scale; /* if non-zero, values are scaled by this */
712 struct profile_data {
715 struct profile_entry e[0];
718 static struct profile_data *prof_data;
720 /*! \brief allocates a counter with a given name and scale.
721 * \return Returns the identifier of the counter.
723 int ast_add_profile(const char *name, uint64_t scale)
725 int l = sizeof(struct profile_data);
726 int n = 10; /* default entries */
728 if (prof_data == NULL) {
729 prof_data = ast_calloc(1, l + n*sizeof(struct profile_entry));
730 if (prof_data == NULL)
732 prof_data->entries = 0;
733 prof_data->max_size = n;
735 if (prof_data->entries >= prof_data->max_size) {
737 n = prof_data->max_size + 20;
738 p = ast_realloc(prof_data, l + n*sizeof(struct profile_entry));
742 prof_data->max_size = n;
744 n = prof_data->entries++;
745 prof_data->e[n].name = ast_strdup(name);
746 prof_data->e[n].value = 0;
747 prof_data->e[n].events = 0;
748 prof_data->e[n].mark = 0;
749 prof_data->e[n].scale = scale;
753 int64_t ast_profile(int i, int64_t delta)
755 if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
757 if (prof_data->e[i].scale > 1)
758 delta /= prof_data->e[i].scale;
759 prof_data->e[i].value += delta;
760 prof_data->e[i].events++;
761 return prof_data->e[i].value;
764 /* The RDTSC instruction was introduced on the Pentium processor and is not
765 * implemented on certain clones, like the Cyrix 586. Hence, the previous
766 * expectation of __i386__ was in error. */
767 #if defined ( __i686__) && (defined(__FreeBSD__) || defined(linux))
768 #if defined(__FreeBSD__)
769 #include <machine/cpufunc.h>
771 static __inline uint64_t
776 __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
780 #else /* supply a dummy function on other platforms */
781 static __inline uint64_t
788 int64_t ast_mark(int i, int startstop)
790 if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
793 prof_data->e[i].mark = rdtsc();
795 prof_data->e[i].mark = (rdtsc() - prof_data->e[i].mark);
796 if (prof_data->e[i].scale > 1)
797 prof_data->e[i].mark /= prof_data->e[i].scale;
798 prof_data->e[i].value += prof_data->e[i].mark;
799 prof_data->e[i].events++;
801 return prof_data->e[i].mark;
804 #define DEFINE_PROFILE_MIN_MAX_VALUES min = 0; \
805 max = prof_data->entries;\
806 if (a->argc > 3) { /* specific entries */ \
807 if (isdigit(a->argv[3][0])) { \
808 min = atoi(a->argv[3]); \
809 if (a->argc == 5 && strcmp(a->argv[4], "-")) \
810 max = atoi(a->argv[4]); \
812 search = a->argv[3]; \
814 if (max > prof_data->entries) \
815 max = prof_data->entries;
817 static char *handle_show_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
820 const char *search = NULL;
823 e->command = "core show profile";
824 e->usage = "Usage: core show profile\n"
825 " show profile information";
831 if (prof_data == NULL)
834 DEFINE_PROFILE_MIN_MAX_VALUES;
835 ast_cli(a->fd, "profile values (%d, allocated %d)\n-------------------\n",
836 prof_data->entries, prof_data->max_size);
837 ast_cli(a->fd, "%6s %8s %10s %12s %12s %s\n", "ID", "Scale", "Events",
838 "Value", "Average", "Name");
839 for (i = min; i < max; i++) {
840 struct profile_entry *entry = &prof_data->e[i];
841 if (!search || strstr(entry->name, search))
842 ast_cli(a->fd, "%6d: [%8ld] %10ld %12lld %12lld %s\n",
845 (long)entry->events, (long long)entry->value,
846 (long long)(entry->events ? entry->value / entry->events : entry->value),
852 static char *handle_clear_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
855 const char *search = NULL;
858 e->command = "core clear profile";
859 e->usage = "Usage: core clear profile\n"
860 " clear profile information";
866 if (prof_data == NULL)
869 DEFINE_PROFILE_MIN_MAX_VALUES;
870 for (i= min; i < max; i++) {
871 if (!search || strstr(prof_data->e[i].name, search)) {
872 prof_data->e[i].value = 0;
873 prof_data->e[i].events = 0;
878 #undef DEFINE_PROFILE_MIN_MAX_VALUES
880 /*! \brief CLI command to list module versions */
881 static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
883 #define FORMAT "%-25.25s %-40.40s\n"
884 struct file_version *iterator;
890 int matchlen, which = 0;
891 struct file_version *find;
895 e->command = "core show file version [like]";
897 "Usage: core show file version [like <pattern>]\n"
898 " Lists the revision numbers of the files used to build this copy of Asterisk.\n"
899 " Optional regular expression pattern is used to filter the file list.\n";
902 matchlen = strlen(a->word);
905 AST_RWLIST_RDLOCK(&file_versions);
906 AST_RWLIST_TRAVERSE(&file_versions, find, list) {
907 if (!strncasecmp(a->word, find->file, matchlen) && ++which > a->n) {
908 ret = ast_strdup(find->file);
912 AST_RWLIST_UNLOCK(&file_versions);
919 if (!strcasecmp(a->argv[4], "like")) {
920 if (regcomp(®exbuf, a->argv[5], REG_EXTENDED | REG_NOSUB))
921 return CLI_SHOWUSAGE;
924 return CLI_SHOWUSAGE;
932 return CLI_SHOWUSAGE;
935 ast_cli(a->fd, FORMAT, "File", "Revision");
936 ast_cli(a->fd, FORMAT, "----", "--------");
937 AST_RWLIST_RDLOCK(&file_versions);
938 AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
939 if (havename && strcasecmp(iterator->file, a->argv[4]))
942 if (havepattern && regexec(®exbuf, iterator->file, 0, NULL, 0))
945 ast_cli(a->fd, FORMAT, iterator->file, iterator->version);
950 AST_RWLIST_UNLOCK(&file_versions);
952 ast_cli(a->fd, "%d files listed.\n", count_files);
962 #endif /* ! LOW_MEMORY */
964 int ast_register_atexit(void (*func)(void))
966 struct ast_atexit *ae;
968 if (!(ae = ast_calloc(1, sizeof(*ae))))
973 ast_unregister_atexit(func);
975 AST_RWLIST_WRLOCK(&atexits);
976 AST_RWLIST_INSERT_HEAD(&atexits, ae, list);
977 AST_RWLIST_UNLOCK(&atexits);
982 void ast_unregister_atexit(void (*func)(void))
984 struct ast_atexit *ae = NULL;
986 AST_RWLIST_WRLOCK(&atexits);
987 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
988 if (ae->func == func) {
989 AST_RWLIST_REMOVE_CURRENT(list);
993 AST_RWLIST_TRAVERSE_SAFE_END;
994 AST_RWLIST_UNLOCK(&atexits);
999 /* Sending commands from consoles back to the daemon requires a terminating NULL */
1000 static int fdsend(int fd, const char *s)
1002 return write(fd, s, strlen(s) + 1);
1005 /* Sending messages from the daemon back to the display requires _excluding_ the terminating NULL */
1006 static int fdprint(int fd, const char *s)
1008 return write(fd, s, strlen(s));
1011 /*! \brief NULL handler so we can collect the child exit status */
1012 static void _null_sig_handler(int sig)
1016 static struct sigaction null_sig_handler = {
1017 .sa_handler = _null_sig_handler,
1018 .sa_flags = SA_RESTART,
1021 static struct sigaction ignore_sig_handler = {
1022 .sa_handler = SIG_IGN,
1025 AST_MUTEX_DEFINE_STATIC(safe_system_lock);
1026 /*! \brief Keep track of how many threads are currently trying to wait*() on
1027 * a child process */
1028 static unsigned int safe_system_level = 0;
1029 static struct sigaction safe_system_prev_handler;
1031 void ast_replace_sigchld(void)
1035 ast_mutex_lock(&safe_system_lock);
1036 level = safe_system_level++;
1038 /* only replace the handler if it has not already been done */
1040 sigaction(SIGCHLD, &null_sig_handler, &safe_system_prev_handler);
1043 ast_mutex_unlock(&safe_system_lock);
1046 void ast_unreplace_sigchld(void)
1050 ast_mutex_lock(&safe_system_lock);
1051 level = --safe_system_level;
1053 /* only restore the handler if we are the last one */
1055 sigaction(SIGCHLD, &safe_system_prev_handler, NULL);
1058 ast_mutex_unlock(&safe_system_lock);
1061 int ast_safe_system(const char *s)
1065 struct rusage rusage;
1068 #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
1069 ast_replace_sigchld();
1071 #ifdef HAVE_WORKING_FORK
1079 cap_t cap = cap_from_text("cap_net_admin-eip");
1081 if (cap_set_proc(cap)) {
1082 /* Careful with order! Logging cannot happen after we close FDs */
1083 ast_log(LOG_WARNING, "Unable to remove capabilities.\n");
1087 #ifdef HAVE_WORKING_FORK
1088 if (ast_opt_high_priority)
1089 ast_set_priority(0);
1090 /* Close file descriptors and launch system command */
1091 ast_close_fds_above_n(STDERR_FILENO);
1093 execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
1095 } else if (pid > 0) {
1097 res = wait4(pid, &status, 0, &rusage);
1099 res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
1101 } else if (errno != EINTR)
1105 ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
1109 ast_unreplace_sigchld();
1110 #else /* !defined(HAVE_WORKING_FORK) && !defined(HAVE_WORKING_VFORK) */
1118 * \brief enable or disable a logging level to a specified console
1120 void ast_console_toggle_loglevel(int fd, int level, int state)
1124 if (level >= NUMLOGLEVELS) {
1125 level = NUMLOGLEVELS - 1;
1128 for (x = 0;x < AST_MAX_CONNECTS; x++) {
1129 if (fd == consoles[x].fd) {
1131 * Since the logging occurs when levels are false, set to
1132 * flipped iinput because this function accepts 0 as off and 1 as on
1134 consoles[x].levels[level] = state ? 0 : 1;
1141 * \brief mute or unmute a console from logging
1143 void ast_console_toggle_mute(int fd, int silent)
1146 for (x = 0;x < AST_MAX_CONNECTS; x++) {
1147 if (fd == consoles[x].fd) {
1148 if (consoles[x].mute) {
1149 consoles[x].mute = 0;
1151 ast_cli(fd, "Console is not muted anymore.\n");
1153 consoles[x].mute = 1;
1155 ast_cli(fd, "Console is muted.\n");
1160 ast_cli(fd, "Couldn't find remote console.\n");
1164 * \brief log the string to all attached console clients
1166 static void ast_network_puts_mutable(const char *string, int level)
1169 for (x = 0;x < AST_MAX_CONNECTS; x++) {
1170 if (consoles[x].mute)
1172 if (consoles[x].fd > -1) {
1173 if (!consoles[x].levels[level])
1174 fdprint(consoles[x].p[1], string);
1180 * \brief log the string to the console, and all attached
1183 void ast_console_puts_mutable(const char *string, int level)
1185 fputs(string, stdout);
1187 ast_network_puts_mutable(string, level);
1191 * \brief write the string to all attached console clients
1193 static void ast_network_puts(const char *string)
1196 for (x = 0; x < AST_MAX_CONNECTS; x++) {
1197 if (consoles[x].fd > -1)
1198 fdprint(consoles[x].p[1], string);
1203 * write the string to the console, and all attached
1206 void ast_console_puts(const char *string)
1208 fputs(string, stdout);
1210 ast_network_puts(string);
1213 static void network_verboser(const char *s)
1215 ast_network_puts_mutable(s, __LOG_VERBOSE);
1218 static pthread_t lthread;
1221 * \brief read() function supporting the reception of user credentials.
1223 * \param fd Socket file descriptor.
1224 * \param buffer Receive buffer.
1225 * \param size 'buffer' size.
1226 * \param con Console structure to set received credentials
1227 * \retval -1 on error
1228 * \retval the number of bytes received on success.
1230 static int read_credentials(int fd, char *buffer, size_t size, struct console *con)
1232 #if defined(SO_PEERCRED)
1233 #ifdef HAVE_STRUCT_SOCKPEERCRED_UID
1234 #define HAVE_STRUCT_UCRED_UID
1235 struct sockpeercred cred;
1239 socklen_t len = sizeof(cred);
1241 #if defined(HAVE_GETPEEREID)
1249 result = read(fd, buffer, size);
1254 #if defined(SO_PEERCRED) && (defined(HAVE_STRUCT_UCRED_UID) || defined(HAVE_STRUCT_UCRED_CR_UID))
1255 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len)) {
1258 #if defined(HAVE_STRUCT_UCRED_UID)
1261 #else /* defined(HAVE_STRUCT_UCRED_CR_UID) */
1264 #endif /* defined(HAVE_STRUCT_UCRED_UID) */
1266 #elif defined(HAVE_GETPEEREID)
1267 if (getpeereid(fd, &uid, &gid)) {
1279 static void *netconsole(void *vconsole)
1281 struct console *con = vconsole;
1282 char hostname[MAXHOSTNAMELEN] = "";
1285 const char * const end_buf = inbuf + sizeof(inbuf);
1286 char *start_read = inbuf;
1288 struct pollfd fds[2];
1290 if (gethostname(hostname, sizeof(hostname)-1))
1291 ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
1292 snprintf(outbuf, sizeof(outbuf), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
1293 fdprint(con->fd, outbuf);
1295 fds[0].fd = con->fd;
1296 fds[0].events = POLLIN;
1298 fds[1].fd = con->p[0];
1299 fds[1].events = POLLIN;
1302 res = ast_poll(fds, 2, -1);
1305 ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
1308 if (fds[0].revents) {
1309 int cmds_read, bytes_read;
1310 if ((bytes_read = read_credentials(con->fd, start_read, end_buf - start_read, con)) < 1) {
1313 /* XXX This will only work if it is the first command, and I'm not sure fixing it is worth the effort. */
1314 if (strncmp(inbuf, "cli quit after ", 15) == 0) {
1315 ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read - 15, inbuf + 15);
1318 /* ast_cli_command_multiple_full will only process individual commands terminated by a
1319 * NULL and not trailing partial commands. */
1320 if (!(cmds_read = ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read + start_read - inbuf, inbuf))) {
1321 /* No commands were read. We either have a short read on the first command
1322 * with space left, or a command that is too long */
1323 if (start_read + bytes_read < end_buf) {
1324 start_read += bytes_read;
1326 ast_log(LOG_ERROR, "Command too long! Skipping\n");
1331 if (start_read[bytes_read - 1] == '\0') {
1332 /* The read ended on a command boundary, start reading again at the head of inbuf */
1336 /* If we get this far, we have left over characters that have not been processed.
1337 * Advance to the character after the last command read by ast_cli_command_multiple_full.
1338 * We are guaranteed to have at least cmds_read NULLs */
1339 while (cmds_read-- && (start_read = strchr(start_read, '\0'))) {
1342 memmove(inbuf, start_read, end_buf - start_read);
1343 start_read = end_buf - start_read + inbuf;
1345 if (fds[1].revents) {
1346 res = read_credentials(con->p[0], outbuf, sizeof(outbuf), con);
1348 ast_log(LOG_ERROR, "read returned %d\n", res);
1351 res = write(con->fd, outbuf, res);
1356 if (!ast_opt_hide_connect) {
1357 ast_verb(3, "Remote UNIX connection disconnected\n");
1367 static void *listener(void *unused)
1369 struct sockaddr_un sunaddr;
1374 struct pollfd fds[1];
1378 fds[0].fd = ast_socket;
1379 fds[0].events = POLLIN;
1380 s = ast_poll(fds, 1, -1);
1381 pthread_testcancel();
1384 ast_log(LOG_WARNING, "poll returned error: %s\n", strerror(errno));
1387 len = sizeof(sunaddr);
1388 s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
1391 ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
1393 #if !defined(SO_PASSCRED)
1397 /* turn on socket credentials passing. */
1398 if (setsockopt(s, SOL_SOCKET, SO_PASSCRED, &sckopt, sizeof(sckopt)) < 0) {
1399 ast_log(LOG_WARNING, "Unable to turn on socket credentials passing\n");
1402 for (x = 0; x < AST_MAX_CONNECTS; x++) {
1403 if (consoles[x].fd >= 0) {
1406 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
1407 ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
1408 consoles[x].fd = -1;
1409 fdprint(s, "Server failed to create pipe\n");
1413 flags = fcntl(consoles[x].p[1], F_GETFL);
1414 fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
1416 consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */
1417 /* Default uid and gid to -2, so then in cli.c/cli_has_permissions() we will be able
1418 to know if the user didn't send the credentials. */
1419 consoles[x].uid = -2;
1420 consoles[x].gid = -2;
1421 if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) {
1422 ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
1423 close(consoles[x].p[0]);
1424 close(consoles[x].p[1]);
1425 consoles[x].fd = -1;
1426 fdprint(s, "Server failed to spawn thread\n");
1431 if (x >= AST_MAX_CONNECTS) {
1432 fdprint(s, "No more connections allowed\n");
1433 ast_log(LOG_WARNING, "No more connections allowed\n");
1435 } else if ((consoles[x].fd > -1) && (!ast_opt_hide_connect)) {
1436 ast_verb(3, "Remote UNIX connection\n");
1444 static int ast_makesocket(void)
1446 struct sockaddr_un sunaddr;
1452 for (x = 0; x < AST_MAX_CONNECTS; x++)
1453 consoles[x].fd = -1;
1454 unlink(ast_config_AST_SOCKET);
1455 ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
1456 if (ast_socket < 0) {
1457 ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
1460 memset(&sunaddr, 0, sizeof(sunaddr));
1461 sunaddr.sun_family = AF_LOCAL;
1462 ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
1463 res = bind(ast_socket, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
1465 ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
1470 res = listen(ast_socket, 2);
1472 ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
1477 if (ast_register_verbose(network_verboser)) {
1478 ast_log(LOG_WARNING, "Unable to register network verboser?\n");
1481 if (ast_pthread_create_background(<hread, NULL, listener, NULL)) {
1482 ast_log(LOG_WARNING, "Unable to create listener thread.\n");
1487 if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
1489 if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL)
1490 ast_log(LOG_WARNING, "Unable to find uid of user %s\n", ast_config_AST_CTL_OWNER);
1495 if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
1497 if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL)
1498 ast_log(LOG_WARNING, "Unable to find gid of group %s\n", ast_config_AST_CTL_GROUP);
1503 if (chown(ast_config_AST_SOCKET, uid, gid) < 0)
1504 ast_log(LOG_WARNING, "Unable to change ownership of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
1506 if (!ast_strlen_zero(ast_config_AST_CTL_PERMISSIONS)) {
1509 sscanf(ast_config_AST_CTL_PERMISSIONS, "%30o", &p1);
1511 if ((chmod(ast_config_AST_SOCKET, p)) < 0)
1512 ast_log(LOG_WARNING, "Unable to change file permissions of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
1518 static int ast_tryconnect(void)
1520 struct sockaddr_un sunaddr;
1522 ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
1523 if (ast_consock < 0) {
1524 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
1527 memset(&sunaddr, 0, sizeof(sunaddr));
1528 sunaddr.sun_family = AF_LOCAL;
1529 ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
1530 res = connect(ast_consock, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
1539 /*! \brief Urgent handler
1541 Called by soft_hangup to interrupt the poll, read, or other
1542 system call. We don't actually need to do anything though.
1543 Remember: Cannot EVER ast_log from within a signal handler
1545 static void _urg_handler(int num)
1550 static struct sigaction urg_handler = {
1551 .sa_handler = _urg_handler,
1552 .sa_flags = SA_RESTART,
1555 static void _hup_handler(int num)
1557 int a = 0, save_errno = errno;
1558 printf("Received HUP signal -- Reloading configs\n");
1560 execvp(_argv[0], _argv);
1561 sig_flags.need_reload = 1;
1562 if (sig_alert_pipe[1] != -1) {
1563 if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) {
1564 fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno));
1570 static struct sigaction hup_handler = {
1571 .sa_handler = _hup_handler,
1572 .sa_flags = SA_RESTART,
1575 static void _child_handler(int sig)
1577 /* Must not ever ast_log or ast_verbose within signal handler */
1578 int n, status, save_errno = errno;
1581 * Reap all dead children -- not just one
1583 for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
1585 if (n == 0 && option_debug)
1586 printf("Huh? Child handler, but nobody there?\n");
1590 static struct sigaction child_handler = {
1591 .sa_handler = _child_handler,
1592 .sa_flags = SA_RESTART,
1595 /*! \brief Set maximum open files */
1596 static void set_ulimit(int value)
1598 struct rlimit l = {0, 0};
1601 ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
1608 if (setrlimit(RLIMIT_NOFILE, &l)) {
1609 ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
1613 ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
1618 /*! \brief Set an X-term or screen title */
1619 static void set_title(char *text)
1621 if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
1622 fprintf(stdout, "\033]2;%s\007", text);
1625 static void set_icon(char *text)
1627 if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
1628 fprintf(stdout, "\033]1;%s\007", text);
1631 /*! \brief We set ourselves to a high priority, that we might pre-empt everything
1632 else. If your PBX has heavy activity on it, this is a good thing. */
1633 int ast_set_priority(int pri)
1635 struct sched_param sched;
1636 memset(&sched, 0, sizeof(sched));
1639 sched.sched_priority = 10;
1640 if (sched_setscheduler(0, SCHED_RR, &sched)) {
1641 ast_log(LOG_WARNING, "Unable to set high priority\n");
1644 ast_verb(1, "Set to realtime thread\n");
1646 sched.sched_priority = 0;
1647 /* According to the manpage, these parameters can never fail. */
1648 sched_setscheduler(0, SCHED_OTHER, &sched);
1652 if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
1653 ast_log(LOG_WARNING, "Unable to set high priority\n");
1656 ast_verb(1, "Set to high priority\n");
1658 /* According to the manpage, these parameters can never fail. */
1659 setpriority(PRIO_PROCESS, 0, 0);
1665 static void ast_run_atexits(void)
1667 struct ast_atexit *ae;
1668 AST_RWLIST_RDLOCK(&atexits);
1669 AST_RWLIST_TRAVERSE(&atexits, ae, list) {
1673 AST_RWLIST_UNLOCK(&atexits);
1676 static int can_safely_quit(shutdown_nice_t niceness, int restart);
1677 static void really_quit(int num, shutdown_nice_t niceness, int restart);
1679 static void quit_handler(int num, shutdown_nice_t niceness, int restart)
1681 if (can_safely_quit(niceness, restart)) {
1682 really_quit(num, niceness, restart);
1683 /* No one gets here. */
1685 /* It wasn't our time. */
1688 static int can_safely_quit(shutdown_nice_t niceness, int restart)
1690 /* Check if someone else isn't already doing this. */
1691 ast_mutex_lock(&safe_system_lock);
1692 if (shuttingdown != NOT_SHUTTING_DOWN && niceness >= shuttingdown) {
1693 /* Already in progress and other request was less nice. */
1694 ast_mutex_unlock(&safe_system_lock);
1695 ast_verbose("Ignoring asterisk %s request, already in progress.\n", restart ? "restart" : "shutdown");
1698 shuttingdown = niceness;
1699 ast_mutex_unlock(&safe_system_lock);
1701 /* Try to get as many CDRs as possible submitted to the backend engines
1702 * (if in batch mode). really_quit happens to call it again when running
1703 * the atexit handlers, otherwise this would be a bit early. */
1704 ast_cdr_engine_term();
1707 if (niceness == SHUTDOWN_NORMAL) {
1709 /* Begin shutdown routine, hanging up active channels */
1710 ast_begin_shutdown(1);
1711 if (option_verbose && ast_opt_console) {
1712 ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
1717 /* Wait up to 15 seconds for all channels to go away */
1718 if ((e - s) > 15 || !ast_undestroyed_channels() || shuttingdown != niceness) {
1721 /* Sleep 1/10 of a second */
1724 } else if (niceness >= SHUTDOWN_NICE) {
1725 if (niceness != SHUTDOWN_REALLY_NICE) {
1726 ast_begin_shutdown(0);
1728 if (option_verbose && ast_opt_console) {
1729 ast_verb(0, "Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
1732 if (!ast_undestroyed_channels() || shuttingdown != niceness) {
1739 /* Re-acquire lock and check if someone changed the niceness, in which
1740 * case someone else has taken over the shutdown. */
1741 ast_mutex_lock(&safe_system_lock);
1742 if (shuttingdown != niceness) {
1743 if (shuttingdown == NOT_SHUTTING_DOWN && option_verbose && ast_opt_console) {
1744 ast_verb(0, "Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
1746 ast_mutex_unlock(&safe_system_lock);
1749 shuttingdown = SHUTTING_DOWN;
1750 ast_mutex_unlock(&safe_system_lock);
1755 static void really_quit(int num, shutdown_nice_t niceness, int restart)
1757 if (niceness >= SHUTDOWN_NICE) {
1758 ast_module_shutdown();
1761 if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) {
1762 char filename[80] = "";
1763 if (getenv("HOME")) {
1764 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
1766 if (!ast_strlen_zero(filename)) {
1767 ast_el_write_history(filename);
1769 if (consolethread == AST_PTHREADT_NULL || consolethread == pthread_self()) {
1770 /* Only end if we are the consolethread, otherwise there's a race with that thread. */
1774 if (el_hist != NULL) {
1775 history_end(el_hist);
1777 } else if (mon_sig_flags == pthread_self()) {
1778 if (consolethread != AST_PTHREADT_NULL) {
1779 pthread_kill(consolethread, SIGURG);
1783 ast_verb(0, "Executing last minute cleanups\n");
1785 /* Called on exit */
1786 ast_verb(0, "Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
1787 ast_debug(1, "Asterisk ending (%d).\n", num);
1789 <managerEventInstance>
1790 <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis>
1792 <parameter name="Shutdown">
1794 <enum name="Uncleanly"/>
1795 <enum name="Cleanly"/>
1798 <parameter name="Restart">
1801 <enum name="False"/>
1805 </managerEventInstance>
1807 manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False");
1808 if (ast_socket > -1) {
1809 pthread_cancel(lthread);
1812 unlink(ast_config_AST_SOCKET);
1814 if (ast_consock > -1)
1816 if (!ast_opt_remote)
1817 unlink(ast_config_AST_PID);
1818 printf("%s", term_quit());
1821 ast_verb(0, "Preparing for Asterisk restart...\n");
1822 /* Mark all FD's for closing on exec */
1823 for (i = 3; i < 32768; i++) {
1824 fcntl(i, F_SETFD, FD_CLOEXEC);
1826 ast_verb(0, "Asterisk is now restarting...\n");
1832 /* If there is a consolethread running send it a SIGHUP
1833 so it can execvp, otherwise we can do it ourselves */
1834 if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
1835 pthread_kill(consolethread, SIGHUP);
1836 /* Give the signal handler some time to complete */
1839 execvp(_argv[0], _argv);
1849 static void __quit_handler(int num)
1852 sig_flags.need_quit = 1;
1853 if (sig_alert_pipe[1] != -1) {
1854 if (write(sig_alert_pipe[1], &a, sizeof(a)) < 0) {
1855 fprintf(stderr, "quit_handler: write() failed: %s\n", strerror(errno));
1858 /* There is no need to restore the signal handler here, since the app
1859 * is going to exit */
1862 static void __remote_quit_handler(int num)
1864 sig_flags.need_quit = 1;
1867 static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
1871 if (!strncmp(s, cmp, strlen(cmp))) {
1872 c = s + strlen(cmp);
1873 term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
1879 /* These gymnastics are due to platforms which designate char as unsigned by
1880 * default. Level is the negative character -- offset by 1, because \0 is the
1882 #define VERBOSE_MAGIC2LEVEL(x) (((char) -*(signed char *) (x)) - 1)
1883 #define VERBOSE_HASMAGIC(x) (*(signed char *) (x) < 0)
1885 static void console_verboser(const char *s)
1888 const char *c = NULL;
1891 if (VERBOSE_HASMAGIC(s)) {
1892 level = VERBOSE_MAGIC2LEVEL(s);
1894 if (level > option_verbose) {
1899 if ((c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_4)) ||
1900 (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_3)) ||
1901 (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_2)) ||
1902 (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_1))) {
1911 /* Wake up a poll()ing console */
1912 if (ast_opt_console && consolethread != AST_PTHREADT_NULL) {
1913 pthread_kill(consolethread, SIGURG);
1917 static int ast_all_zeros(char *s)
1927 static void consolehandler(char *s)
1929 printf("%s", term_end());
1932 /* Called when readline data is available */
1933 if (!ast_all_zeros(s))
1934 ast_el_add_history(s);
1935 /* The real handler for bang */
1938 ast_safe_system(s+1);
1940 ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
1942 ast_cli_command(STDOUT_FILENO, s);
1945 static int remoteconsolehandler(char *s)
1949 /* Called when readline data is available */
1950 if (!ast_all_zeros(s))
1951 ast_el_add_history(s);
1952 /* The real handler for bang */
1955 ast_safe_system(s+1);
1957 ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
1959 } else if (strncasecmp(s, "core set verbose ", 17) == 0) {
1960 int old_verbose = option_verbose;
1961 if (strncasecmp(s + 17, "atleast ", 8) == 0) {
1963 if (sscanf(s + 25, "%d", &tmp) != 1) {
1964 fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
1966 if (tmp > option_verbose) {
1967 option_verbose = tmp;
1969 if (old_verbose != option_verbose) {
1970 fprintf(stdout, "Set remote console verbosity from %d to %d\n", old_verbose, option_verbose);
1972 fprintf(stdout, "Verbosity level unchanged.\n");
1976 if (sscanf(s + 17, "%d", &option_verbose) != 1) {
1977 fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
1979 if (old_verbose != option_verbose) {
1980 fprintf(stdout, "Set remote console verbosity to %d\n", option_verbose);
1982 fprintf(stdout, "Verbosity level unchanged.\n");
1987 } else if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
1988 (s[4] == '\0' || isspace(s[4]))) {
1989 quit_handler(0, SHUTDOWN_FAST, 0);
1996 static char *handle_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2000 e->command = "core show version";
2002 "Usage: core show version\n"
2003 " Shows Asterisk version information.\n";
2010 return CLI_SHOWUSAGE;
2011 ast_cli(a->fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
2012 ast_get_version(), ast_build_user, ast_build_hostname,
2013 ast_build_machine, ast_build_os, ast_build_date);
2018 static int handle_quit(int fd, int argc, char *argv[])
2021 return RESULT_SHOWUSAGE;
2022 quit_handler(0, SHUTDOWN_NORMAL, 0);
2023 return RESULT_SUCCESS;
2027 static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2031 e->command = "core stop now";
2033 "Usage: core stop now\n"
2034 " Shuts down a running Asterisk immediately, hanging up all active calls .\n";
2040 if (a->argc != e->args)
2041 return CLI_SHOWUSAGE;
2042 quit_handler(0, SHUTDOWN_NORMAL, 0 /* not restart */);
2046 static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2050 e->command = "core stop gracefully";
2052 "Usage: core stop gracefully\n"
2053 " Causes Asterisk to not accept new calls, and exit when all\n"
2054 " active calls have terminated normally.\n";
2060 if (a->argc != e->args)
2061 return CLI_SHOWUSAGE;
2062 quit_handler(0, SHUTDOWN_NICE, 0 /* no restart */);
2066 static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2070 e->command = "core stop when convenient";
2072 "Usage: core stop when convenient\n"
2073 " Causes Asterisk to perform a shutdown when all active calls have ended.\n";
2079 if (a->argc != e->args)
2080 return CLI_SHOWUSAGE;
2081 ast_cli(a->fd, "Waiting for inactivity to perform halt\n");
2082 quit_handler(0, SHUTDOWN_REALLY_NICE, 0 /* don't restart */);
2086 static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2090 e->command = "core restart now";
2092 "Usage: core restart now\n"
2093 " Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
2100 if (a->argc != e->args)
2101 return CLI_SHOWUSAGE;
2102 quit_handler(0, SHUTDOWN_NORMAL, 1 /* restart */);
2106 static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2110 e->command = "core restart gracefully";
2112 "Usage: core restart gracefully\n"
2113 " Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
2114 " restart when all active calls have ended.\n";
2120 if (a->argc != e->args)
2121 return CLI_SHOWUSAGE;
2122 quit_handler(0, SHUTDOWN_NICE, 1 /* restart */);
2126 static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2130 e->command = "core restart when convenient";
2132 "Usage: core restart when convenient\n"
2133 " Causes Asterisk to perform a cold restart when all active calls have ended.\n";
2139 if (a->argc != e->args)
2140 return CLI_SHOWUSAGE;
2141 ast_cli(a->fd, "Waiting for inactivity to perform restart\n");
2142 quit_handler(0, SHUTDOWN_REALLY_NICE, 1 /* restart */);
2146 static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2148 int aborting_shutdown = 0;
2152 e->command = "core abort shutdown";
2154 "Usage: core abort shutdown\n"
2155 " Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
2156 " call operations.\n";
2162 if (a->argc != e->args)
2163 return CLI_SHOWUSAGE;
2165 ast_mutex_lock(&safe_system_lock);
2166 if (shuttingdown >= SHUTDOWN_FAST) {
2167 aborting_shutdown = 1;
2168 shuttingdown = NOT_SHUTTING_DOWN;
2170 ast_mutex_unlock(&safe_system_lock);
2172 if (aborting_shutdown) {
2173 ast_cancel_shutdown();
2178 static char *handle_bang(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2184 "Usage: !<command>\n"
2185 " Executes a given shell command\n";
2193 static const char warranty_lines[] = {
2197 "BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n"
2198 "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n"
2199 "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n"
2200 "PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n"
2201 "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"
2202 "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n"
2203 "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n"
2204 "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n"
2205 "REPAIR OR CORRECTION.\n"
2207 "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n"
2208 "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n"
2209 "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n"
2210 "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n"
2211 "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n"
2212 "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n"
2213 "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n"
2214 "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n"
2215 "POSSIBILITY OF SUCH DAMAGES.\n"
2218 static char *show_warranty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2222 e->command = "core show warranty";
2224 "Usage: core show warranty\n"
2225 " Shows the warranty (if any) for this copy of Asterisk.\n";
2231 ast_cli(a->fd, "%s", warranty_lines);
2236 static const char license_lines[] = {
2238 "This program is free software; you can redistribute it and/or modify\n"
2239 "it under the terms of the GNU General Public License version 2 as\n"
2240 "published by the Free Software Foundation.\n"
2242 "This program also contains components licensed under other licenses.\n"
2245 "This program is distributed in the hope that it will be useful,\n"
2246 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2247 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
2248 "GNU General Public License for more details.\n"
2250 "You should have received a copy of the GNU General Public License\n"
2251 "along with this program; if not, write to the Free Software\n"
2252 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
2255 static char *show_license(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2259 e->command = "core show license";
2261 "Usage: core show license\n"
2262 " Shows the license(s) for this copy of Asterisk.\n";
2268 ast_cli(a->fd, "%s", license_lines);
2273 #define ASTERISK_PROMPT "*CLI> "
2275 #define ASTERISK_PROMPT2 "%s*CLI> "
2277 static struct ast_cli_entry cli_asterisk[] = {
2278 AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
2279 AST_CLI_DEFINE(handle_stop_now, "Shut down Asterisk immediately"),
2280 AST_CLI_DEFINE(handle_stop_gracefully, "Gracefully shut down Asterisk"),
2281 AST_CLI_DEFINE(handle_stop_when_convenient, "Shut down Asterisk at empty call volume"),
2282 AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"),
2283 AST_CLI_DEFINE(handle_restart_gracefully, "Restart Asterisk gracefully"),
2284 AST_CLI_DEFINE(handle_restart_when_convenient, "Restart Asterisk at empty call volume"),
2285 AST_CLI_DEFINE(show_warranty, "Show the warranty (if any) for this copy of Asterisk"),
2286 AST_CLI_DEFINE(show_license, "Show the license(s) for this copy of Asterisk"),
2287 AST_CLI_DEFINE(handle_version, "Display version info"),
2288 AST_CLI_DEFINE(handle_bang, "Execute a shell command"),
2289 #if !defined(LOW_MEMORY)
2290 AST_CLI_DEFINE(handle_show_version_files, "List versions of files used to build Asterisk"),
2291 AST_CLI_DEFINE(handle_show_threads, "Show running threads"),
2292 #if defined(HAVE_SYSINFO) || defined(HAVE_SYSCTL)
2293 AST_CLI_DEFINE(handle_show_sysinfo, "Show System Information"),
2295 AST_CLI_DEFINE(handle_show_profile, "Display profiling info"),
2296 AST_CLI_DEFINE(handle_show_settings, "Show some core settings"),
2297 AST_CLI_DEFINE(handle_clear_profile, "Clear profiling info"),
2298 #endif /* ! LOW_MEMORY */
2301 struct el_read_char_state_struct {
2302 unsigned int line_full:1;
2303 unsigned int prev_line_full:1;
2304 char prev_line_verbosity;
2307 static int el_read_char_state_init(void *ptr)
2309 struct el_read_char_state_struct *state = ptr;
2310 state->line_full = 1;
2311 state->prev_line_full = 1;
2312 state->prev_line_verbosity = 0;
2316 AST_THREADSTORAGE_CUSTOM(el_read_char_state, el_read_char_state_init, ast_free_ptr);
2318 static int ast_el_read_char(EditLine *editline, char *cp)
2322 struct pollfd fds[2];
2325 #define EL_BUF_SIZE 512
2326 char buf[EL_BUF_SIZE];
2327 struct el_read_char_state_struct *state = ast_threadstorage_get(&el_read_char_state, sizeof(*state));
2331 fds[0].fd = ast_consock;
2332 fds[0].events = POLLIN;
2333 if (!ast_opt_exec) {
2334 fds[1].fd = STDIN_FILENO;
2335 fds[1].events = POLLIN;
2338 res = ast_poll(fds, max, -1);
2340 if (sig_flags.need_quit || sig_flags.need_quit_handler)
2344 ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
2348 if (!ast_opt_exec && fds[1].revents) {
2349 num_read = read(STDIN_FILENO, cp, 1);
2356 if (fds[0].revents) {
2358 char *curline = buf, *nextline;
2359 res = read(ast_consock, buf, sizeof(buf) - 1);
2360 /* if the remote side disappears exit */
2362 fprintf(stderr, "\nDisconnected from Asterisk server\n");
2363 if (!ast_opt_reconnect) {
2364 quit_handler(0, SHUTDOWN_FAST, 0);
2367 int reconnects_per_second = 20;
2368 fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
2369 for (tries = 0; tries < 30 * reconnects_per_second; tries++) {
2370 if (ast_tryconnect()) {
2371 fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
2372 printf("%s", term_quit());
2375 fdsend(ast_consock, "logger mute silent");
2377 printf("log and verbose output currently muted ('logger mute' to unmute)\n");
2380 usleep(1000000 / reconnects_per_second);
2382 if (tries >= 30 * reconnects_per_second) {
2383 fprintf(stderr, "Failed to reconnect for 30 seconds. Quitting.\n");
2384 quit_handler(0, SHUTDOWN_FAST, 0);
2392 /* Write over the CLI prompt */
2393 if (!ast_opt_exec && !lastpos) {
2394 if (write(STDOUT_FILENO, "\r
\e[0K", 5) < 0) {
2399 state->prev_line_full = state->line_full;
2400 if ((nextline = strchr(curline, '\n'))) {
2401 state->line_full = 1;
2404 state->line_full = 0;
2405 nextline = strchr(curline, '\0');
2408 if (state->prev_line_full && VERBOSE_HASMAGIC(curline)) {
2409 level = VERBOSE_MAGIC2LEVEL(curline);
2411 } else if (state->prev_line_full && !VERBOSE_HASMAGIC(curline)) {
2412 /* Non-verbose output */
2415 level = state->prev_line_verbosity;
2417 if ((!state->prev_line_full && state->prev_line_verbosity <= option_verbose) || (state->prev_line_full && level <= option_verbose)) {
2418 if (write(STDOUT_FILENO, curline, nextline - curline) < 0) {
2422 state->prev_line_verbosity = level;
2424 } while (!ast_strlen_zero(curline));
2426 if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
2438 static struct ast_str *prompt = NULL;
2440 static char *cli_prompt(EditLine *editline)
2445 static int cli_prompt_changes = 0;
2450 if (prompt == NULL) {
2451 prompt = ast_str_create(100);
2452 } else if (!cli_prompt_changes) {
2453 return ast_str_buffer(prompt);
2455 ast_str_reset(prompt);
2458 if ((pfmt = getenv("ASTERISK_PROMPT"))) {
2460 struct timeval ts = ast_tvnow();
2461 while (*t != '\0') {
2463 char hostname[MAXHOSTNAMELEN] = "";
2465 struct ast_tm tm = { 0, };
2466 int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
2470 case 'C': /* color */
2472 if (sscanf(t, "%30d;%30d%n", &fgcolor, &bgcolor, &i) == 2) {
2473 ast_str_append(&prompt, 0, "%s", term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)));
2475 } else if (sscanf(t, "%30d%n", &fgcolor, &i) == 1) {
2476 ast_str_append(&prompt, 0, "%s", term_color_code(term_code, fgcolor, 0, sizeof(term_code)));
2480 /* If the color has been reset correctly, then there's no need to reset it later */
2481 color_used = ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) ? 0 : 1;
2483 case 'd': /* date */
2484 if (ast_localtime(&ts, &tm, NULL)) {
2485 ast_strftime(tmp, sizeof(tmp), "%Y-%m-%d", &tm);
2486 ast_str_append(&prompt, 0, "%s", tmp);
2487 cli_prompt_changes++;
2490 case 'g': /* group */
2491 if ((gr = getgrgid(getgid()))) {
2492 ast_str_append(&prompt, 0, "%s", gr->gr_name);
2495 case 'h': /* hostname */
2496 if (!gethostname(hostname, sizeof(hostname) - 1)) {
2497 ast_str_append(&prompt, 0, "%s", hostname);
2499 ast_str_append(&prompt, 0, "%s", "localhost");
2502 case 'H': /* short hostname */
2503 if (!gethostname(hostname, sizeof(hostname) - 1)) {
2505 if ((dotptr = strchr(hostname, '.'))) {
2508 ast_str_append(&prompt, 0, "%s", hostname);
2510 ast_str_append(&prompt, 0, "%s", "localhost");
2513 #ifdef HAVE_GETLOADAVG
2514 case 'l': /* load avg */
2516 if (sscanf(t, "%30d", &which) == 1 && which > 0 && which <= 3) {
2518 getloadavg(list, 3);
2519 ast_str_append(&prompt, 0, "%.2f", list[which - 1]);
2520 cli_prompt_changes++;
2524 case 's': /* Asterisk system name (from asterisk.conf) */
2525 ast_str_append(&prompt, 0, "%s", ast_config_AST_SYSTEM_NAME);
2527 case 't': /* time */
2528 if (ast_localtime(&ts, &tm, NULL)) {
2529 ast_strftime(tmp, sizeof(tmp), "%H:%M:%S", &tm);
2530 ast_str_append(&prompt, 0, "%s", tmp);
2531 cli_prompt_changes++;
2534 case 'u': /* username */
2535 if ((pw = getpwuid(getuid()))) {
2536 ast_str_append(&prompt, 0, "%s", pw->pw_name);
2539 case '#': /* process console or remote? */
2540 ast_str_append(&prompt, 0, "%c", ast_opt_remote ? '>' : '#');
2542 case '%': /* literal % */
2543 ast_str_append(&prompt, 0, "%c", '%');
2545 case '\0': /* % is last character - prevent bug */
2550 ast_str_append(&prompt, 0, "%c", *t);
2555 /* Force colors back to normal at end */
2556 ast_str_append(&prompt, 0, "%s", term_color_code(term_code, 0, 0, sizeof(term_code)));
2558 } else if (remotehostname) {
2559 ast_str_set(&prompt, 0, ASTERISK_PROMPT2, remotehostname);
2561 ast_str_set(&prompt, 0, "%s", ASTERISK_PROMPT);
2564 return ast_str_buffer(prompt);
2567 static char **ast_el_strtoarr(char *buf)
2569 char **match_list = NULL, **match_list_tmp, *retstr;
2570 size_t match_list_len;
2574 while ( (retstr = strsep(&buf, " ")) != NULL) {
2576 if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
2578 if (matches + 1 >= match_list_len) {
2579 match_list_len <<= 1;
2580 if ((match_list_tmp = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
2581 match_list = match_list_tmp;
2584 ast_free(match_list);
2585 return (char **) NULL;
2589 match_list[matches++] = ast_strdup(retstr);
2593 return (char **) NULL;
2595 if (matches >= match_list_len) {
2596 if ((match_list_tmp = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
2597 match_list = match_list_tmp;
2600 ast_free(match_list);
2601 return (char **) NULL;
2605 match_list[matches] = (char *) NULL;
2610 static int ast_el_sort_compare(const void *i1, const void *i2)
2614 s1 = ((char **)i1)[0];
2615 s2 = ((char **)i2)[0];
2617 return strcasecmp(s1, s2);
2620 static int ast_cli_display_match_list(char **matches, int len, int max)
2622 int i, idx, limit, count;
2623 int screenwidth = 0;
2624 int numoutput = 0, numoutputline = 0;
2626 screenwidth = ast_get_termcols(STDOUT_FILENO);
2628 /* find out how many entries can be put on one line, with two spaces between strings */
2629 limit = screenwidth / (max + 2);
2633 /* how many lines of output */
2634 count = len / limit;
2635 if (count * limit < len)
2640 qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare);
2642 for (; count > 0; count--) {
2644 for (i = 0; i < limit && matches[idx]; i++, idx++) {
2646 /* Don't print dupes */
2647 if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) {
2649 ast_free(matches[idx]);
2650 matches[idx] = NULL;
2656 fprintf(stdout, "%-*s ", max, matches[idx]);
2657 ast_free(matches[idx]);
2658 matches[idx] = NULL;
2660 if (numoutputline > 0)
2661 fprintf(stdout, "\n");
2668 static char *cli_complete(EditLine *editline, int ch)
2674 int retval = CC_ERROR;
2675 char buf[2048], savechr;
2678 LineInfo *lf = (LineInfo *)el_line(editline);
2680 savechr = *(char *)lf->cursor;
2681 *(char *)lf->cursor = '\0';
2682 ptr = (char *)lf->cursor;
2684 while (ptr > lf->buffer) {
2685 if (isspace(*ptr)) {
2693 len = lf->cursor - ptr;
2695 if (ast_opt_remote) {
2696 snprintf(buf, sizeof(buf), "_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr);
2697 fdsend(ast_consock, buf);
2698 if ((res = read(ast_consock, buf, sizeof(buf) - 1)) < 0) {
2699 return (char*)(CC_ERROR);
2702 nummatches = atoi(buf);
2704 if (nummatches > 0) {
2706 int mlen = 0, maxmbuf = 2048;
2707 /* Start with a 2048 byte buffer */
2708 if (!(mbuf = ast_malloc(maxmbuf))) {
2709 *((char *) lf->cursor) = savechr;
2710 return (char *)(CC_ERROR);
2712 snprintf(buf, sizeof(buf), "_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr);
2713 fdsend(ast_consock, buf);
2716 while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
2717 if (mlen + 1024 > maxmbuf) {
2718 /* Every step increment buffer 1024 bytes */
2720 if (!(mbuf = ast_realloc(mbuf, maxmbuf))) {
2721 *((char *) lf->cursor) = savechr;
2722 return (char *)(CC_ERROR);
2725 /* Only read 1024 bytes at a time */
2726 res = read(ast_consock, mbuf + mlen, 1024);
2732 matches = ast_el_strtoarr(mbuf);
2735 matches = (char **) NULL;
2737 char **p, *oldbuf=NULL;
2739 matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
2740 for (p = matches; p && *p; p++) {
2741 if (!oldbuf || strcmp(*p,oldbuf))
2749 int matches_num, maxlen, match_len;
2751 if (matches[0][0] != '\0') {
2752 el_deletestr(editline, (int) len);
2753 el_insertstr(editline, matches[0]);
2754 retval = CC_REFRESH;
2757 if (nummatches == 1) {
2758 /* Found an exact match */
2759 el_insertstr(editline, " ");
2760 retval = CC_REFRESH;
2762 /* Must be more than one match */
2763 for (i = 1, maxlen = 0; matches[i]; i++) {
2764 match_len = strlen(matches[i]);
2765 if (match_len > maxlen)
2768 matches_num = i - 1;
2769 if (matches_num >1) {
2770 fprintf(stdout, "\n");
2771 ast_cli_display_match_list(matches, nummatches, maxlen);
2772 retval = CC_REDISPLAY;
2774 el_insertstr(editline," ");
2775 retval = CC_REFRESH;
2778 for (i = 0; matches[i]; i++)
2779 ast_free(matches[i]);
2783 *((char *) lf->cursor) = savechr;
2785 return (char *)(long)retval;
2788 static int ast_el_initialize(void)
2791 char *editor, *editrc = getenv("EDITRC");
2793 if (!(editor = getenv("AST_EDITMODE"))) {
2794 if (!(editor = getenv("AST_EDITOR"))) {
2801 if (el_hist != NULL)
2802 history_end(el_hist);
2804 el = el_init("asterisk", stdin, stdout, stderr);
2805 el_set(el, EL_PROMPT, cli_prompt);
2807 el_set(el, EL_EDITMODE, 1);
2808 el_set(el, EL_EDITOR, editor);
2809 el_hist = history_init();
2810 if (!el || !el_hist)
2813 /* setup history with 100 entries */
2814 history(el_hist, &ev, H_SETSIZE, 100);
2816 el_set(el, EL_HIST, history, el_hist);
2818 el_set(el, EL_ADDFN, "ed-complete", "Complete argument", cli_complete);
2819 /* Bind <tab> to command completion */
2820 el_set(el, EL_BIND, "^I", "ed-complete", NULL);
2821 /* Bind ? to command completion */
2822 el_set(el, EL_BIND, "?", "ed-complete", NULL);
2823 /* Bind ^D to redisplay */
2824 el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
2825 /* Bind Delete to delete char left */
2826 el_set(el, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL);
2827 /* Bind Home and End to move to line start and end */
2828 el_set(el, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL);
2829 el_set(el, EL_BIND, "\\e[4~", "ed-move-to-end", NULL);
2830 /* Bind C-left and C-right to move by word (not all terminals) */
2831 el_set(el, EL_BIND, "\\eOC", "vi-next-word", NULL);
2832 el_set(el, EL_BIND, "\\eOD", "vi-prev-word", NULL);
2835 el_source(el, editrc);
2841 #define MAX_HISTORY_COMMAND_LENGTH 256
2843 static int ast_el_add_history(char *buf)
2847 if (el_hist == NULL || el == NULL)
2848 ast_el_initialize();
2849 if (strlen(buf) > (MAX_HISTORY_COMMAND_LENGTH - 1))
2851 return (history(el_hist, &ev, H_ENTER, ast_strip(ast_strdupa(buf))));
2854 static int ast_el_write_history(char *filename)
2858 if (el_hist == NULL || el == NULL)
2859 ast_el_initialize();
2861 return (history(el_hist, &ev, H_SAVE, filename));
2864 static int ast_el_read_history(char *filename)
2868 if (el_hist == NULL || el == NULL) {
2869 ast_el_initialize();
2872 return history(el_hist, &ev, H_LOAD, filename);
2875 static void ast_remotecontrol(char *data)
2879 char filename[80] = "";
2884 char *stringp = NULL;
2889 memset(&sig_flags, 0, sizeof(sig_flags));
2890 signal(SIGINT, __remote_quit_handler);
2891 signal(SIGTERM, __remote_quit_handler);
2892 signal(SIGHUP, __remote_quit_handler);
2894 if (read(ast_consock, buf, sizeof(buf)) < 0) {
2895 ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
2899 char prefix[] = "cli quit after ";
2900 char *tmp = ast_alloca(strlen(data) + strlen(prefix) + 1);
2901 sprintf(tmp, "%s%s", prefix, data);
2902 if (write(ast_consock, tmp, strlen(tmp) + 1) < 0) {
2903 ast_log(LOG_ERROR, "write() failed: %s\n", strerror(errno));
2904 if (sig_flags.need_quit || sig_flags.need_quit_handler) {
2910 hostname = strsep(&stringp, "/");
2911 cpid = strsep(&stringp, "/");
2912 version = strsep(&stringp, "\n");
2914 version = "<Version Unknown>";
2916 strsep(&stringp, ".");
2922 if (!ast_opt_mute) {
2923 fdsend(ast_consock, "logger mute silent");
2925 printf("log and verbose output currently muted ('logger mute' to unmute)\n");
2929 if (ast_opt_exec && data) { /* hack to print output then exit if asterisk -rx is used */
2930 int linefull = 1, prev_linefull = 1, prev_line_verbose = 0;
2932 fds.fd = ast_consock;
2933 fds.events = POLLIN;
2936 while (ast_poll(&fds, 1, 60000) > 0) {
2937 char buffer[512] = "", *curline = buffer, *nextline;
2938 int not_written = 1;
2940 if (sig_flags.need_quit || sig_flags.need_quit_handler) {
2944 if (read(ast_consock, buffer, sizeof(buffer) - 1) <= 0) {
2949 prev_linefull = linefull;
2950 if ((nextline = strchr(curline, '\n'))) {
2955 nextline = strchr(curline, '\0');
2958 /* Skip verbose lines */
2959 /* Prev line full? | Line is verbose | Last line verbose? | Print
2960 * TRUE | TRUE* | TRUE | FALSE
2961 * TRUE | TRUE* | FALSE | FALSE
2962 * TRUE | FALSE* | TRUE | TRUE
2963 * TRUE | FALSE* | FALSE | TRUE
2964 * FALSE | TRUE | TRUE* | FALSE
2965 * FALSE | TRUE | FALSE* | TRUE
2966 * FALSE | FALSE | TRUE* | FALSE
2967 * FALSE | FALSE | FALSE* | TRUE
2969 if ((!prev_linefull && !prev_line_verbose) || (prev_linefull && *curline > 0)) {
2970 prev_line_verbose = 0;
2972 if (write(STDOUT_FILENO, curline, nextline - curline) < 0) {
2973 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
2976 prev_line_verbose = 1;
2979 } while (!ast_strlen_zero(curline));
2981 /* No non-verbose output in 60 seconds. */
2989 ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
2990 remotehostname = hostname;
2992 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
2993 if (el_hist == NULL || el == NULL)
2994 ast_el_initialize();
2996 el_set(el, EL_GETCFN, ast_el_read_char);
2998 if (!ast_strlen_zero(filename))
2999 ast_el_read_history(filename);
3002 ebuf = (char *)el_gets(el, &num);
3004 if (sig_flags.need_quit || sig_flags.need_quit_handler) {
3008 if (!ebuf && write(1, "", 1) < 0)
3011 if (!ast_strlen_zero(ebuf)) {
3012 if (ebuf[strlen(ebuf)-1] == '\n')
3013 ebuf[strlen(ebuf)-1] = '\0';
3014 if (!remoteconsolehandler(ebuf)) {
3015 res = write(ast_consock, ebuf, strlen(ebuf) + 1);
3017 ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
3023 printf("\nDisconnected from Asterisk server\n");
3026 static int show_version(void)
3028 printf("Asterisk %s\n", ast_get_version());
3032 static int show_cli_help(void)
3034 printf("Asterisk %s, Copyright (C) 1999 - 2012, Digium, Inc. and others.\n", ast_get_version());
3035 printf("Usage: asterisk [OPTIONS]\n");
3036 printf("Valid Options:\n");
3037 printf(" -V Display version number and exit\n");
3038 printf(" -C <configfile> Use an alternate configuration file\n");
3039 printf(" -G <group> Run as a group other than the caller\n");
3040 printf(" -U <user> Run as a user other than the caller\n");
3041 printf(" -c Provide console CLI\n");
3042 printf(" -d Enable extra debugging\n");
3043 #if HAVE_WORKING_FORK
3044 printf(" -f Do not fork\n");
3045 printf(" -F Always fork\n");
3047 printf(" -g Dump core in case of a crash\n");
3048 printf(" -h This help screen\n");
3049 printf(" -i Initialize crypto keys at startup\n");
3050 printf(" -I Enable internal timing if DAHDI timer is available\n");
3051 printf(" -L <load> Limit the maximum load average before rejecting new calls\n");
3052 printf(" -M <value> Limit the maximum number of calls to the specified value\n");
3053 printf(" -m Mute debugging and console output on the console\n");
3054 printf(" -n Disable console colorization\n");
3055 printf(" -p Run as pseudo-realtime thread\n");
3056 printf(" -q Quiet mode (suppress output)\n");
3057 printf(" -r Connect to Asterisk on this machine\n");
3058 printf(" -R Same as -r, except attempt to reconnect if disconnected\n");
3059 printf(" -s <socket> Connect to Asterisk via socket <socket> (only valid with -r)\n");
3060 printf(" -t Record soundfiles in /var/tmp and move them where they\n");
3061 printf(" belong after they are done\n");
3062 printf(" -T Display the time in [Mmm dd hh:mm:ss] format for each line\n");
3063 printf(" of output to the CLI\n");
3064 printf(" -v Increase verbosity (multiple v's = more verbose)\n");
3065 printf(" -x <cmd> Execute command <cmd> (implies -r)\n");
3066 printf(" -X Execute includes by default (allows #exec in asterisk.conf)\n");
3067 printf(" -W Adjust terminal colors to compensate for a light background\n");
3072 static void ast_readconfig(void)
3074 struct ast_config *cfg;
3075 struct ast_variable *v;
3076 char *config = DEFAULT_CONFIG_FILE;
3077 char hostname[MAXHOSTNAMELEN] = "";
3078 struct ast_flags config_flags = { CONFIG_FLAG_NOREALTIME };
3080 unsigned int dbdir:1;
3081 unsigned int keydir:1;
3084 /* Set default value */
3085 option_dtmfminduration = AST_MIN_DTMF_DURATION;
3087 if (ast_opt_override_config) {
3088 cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
3089 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
3090 ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
3092 cfg = ast_config_load2(config, "" /* core, can't reload */, config_flags);
3094 /* init with buildtime config */
3095 ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
3096 ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
3097 ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
3098 snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", cfg_paths.spool_dir);
3099 ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
3100 ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
3101 ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
3102 ast_copy_string(cfg_paths.agi_dir, DEFAULT_AGI_DIR, sizeof(cfg_paths.agi_dir));
3103 ast_copy_string(cfg_paths.db_path, DEFAULT_DB, sizeof(cfg_paths.db_path));
3104 ast_copy_string(cfg_paths.sbin_dir, DEFAULT_SBIN_DIR, sizeof(cfg_paths.sbin_dir));
3105 ast_copy_string(cfg_paths.key_dir, DEFAULT_KEY_DIR, sizeof(cfg_paths.key_dir));
3106 ast_copy_string(cfg_paths.pid_path, DEFAULT_PID, sizeof(cfg_paths.pid_path));
3107 ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path));
3108 ast_copy_string(cfg_paths.run_dir, DEFAULT_RUN_DIR, sizeof(cfg_paths.run_dir));
3110 ast_set_default_eid(&ast_eid_default);
3112 /* no asterisk.conf? no problem, use buildtime config! */
3113 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
3117 for (v = ast_variable_browse(cfg, "files"); v; v = v->next) {
3118 if (!strcasecmp(v->name, "astctlpermissions"))
3119 ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
3120 else if (!strcasecmp(v->name, "astctlowner"))
3121 ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
3122 else if (!strcasecmp(v->name, "astctlgroup"))
3123 ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
3124 else if (!strcasecmp(v->name, "astctl"))
3125 ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
3128 for (v = ast_variable_browse(cfg, "directories"); v; v = v->next) {
3129 if (!strcasecmp(v->name, "astetcdir")) {
3130 ast_copy_string(cfg_paths.config_dir, v->value, sizeof(cfg_paths.config_dir));
3131 } else if (!strcasecmp(v->name, "astspooldir")) {
3132 ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
3133 snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", v->value);
3134 } else if (!strcasecmp(v->name, "astvarlibdir")) {
3135 ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
3137 snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
3138 } else if (!strcasecmp(v->name, "astdbdir")) {
3139 snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
3141 } else if (!strcasecmp(v->name, "astdatadir")) {
3142 ast_copy_string(cfg_paths.data_dir, v->value, sizeof(cfg_paths.data_dir));
3144 snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
3145 } else if (!strcasecmp(v->name, "astkeydir")) {
3146 snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
3148 } else if (!strcasecmp(v->name, "astlogdir")) {
3149 ast_copy_string(cfg_paths.log_dir, v->value, sizeof(cfg_paths.log_dir));
3150 } else if (!strcasecmp(v->name, "astagidir")) {
3151 ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir));