Newer
Older
* Asterisk -- An open source telephony toolkit.
Kevin P. Fleming
committed
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/* Doxygenified Copyright Header */
/*!
* \mainpage Asterisk -- An Open Source Telephony Toolkit
*
* \par Developer Documentation for Asterisk
* This is the main developer documentation for Asterisk. It is
* generated by running "make progdocs".
* \par Additional documentation
* \arg \ref DevDoc
* \arg \ref ConfigFiles
*
* \section copyright Copyright and author
*
Olle Johansson
committed
* Copyright (C) 1999 - 2006, Digium, Inc.
* Asterisk is a trade mark registered by Digium, Inc.
*
* \author Mark Spencer <markster@digium.com>
* Also see \ref AstCREDITS
*
* \section license License
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*
* \verbinclude LICENSE
*
*/
/*! \file
\brief Top level source file for Asterisk - the Open Source PBX. Implementation
of PBX core functions and CLI interface.
*/
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <sched.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/resource.h>
#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>
#ifdef linux
#include <sys/prctl.h>
#endif
#if defined(__FreeBSD__) || defined( __NetBSD__ ) || defined(SOLARIS)
#include <netdb.h>
#if defined(SOLARIS)
extern int daemon(int, int); /* defined in libresolv of all places */
#endif
Kevin P. Fleming
committed
#include "asterisk.h"
Kevin P. Fleming
committed
#include "asterisk/logger.h"
#include "asterisk/options.h"
#include "asterisk/cli.h"
#include "asterisk/channel.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
#include "asterisk/image.h"
#include "asterisk/tdd.h"
#include "asterisk/term.h"
#include "asterisk/manager.h"
#include "asterisk/cdr.h"
Kevin P. Fleming
committed
#include "asterisk/pbx.h"
#include "asterisk/enum.h"
#include "asterisk/rtp.h"
#include "asterisk/http.h"
#include "asterisk/udptl.h"
Kevin P. Fleming
committed
#include "asterisk/app.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/file.h"
#include "asterisk/io.h"
#include "asterisk/lock.h"
Kevin P. Fleming
committed
#include "asterisk/config.h"
Kevin P. Fleming
committed
#include "asterisk/version.h"
#include "asterisk/linkedlists.h"
Kevin P. Fleming
committed
#include "asterisk/devicestate.h"
Kevin P. Fleming
committed
#include "asterisk/doxyref.h" /* Doxygen documentation */
Kevin P. Fleming
committed
#include "defaults.h"
Mark Spencer
committed
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#define PF_LOCAL PF_UNIX
#endif
#define AST_MAX_CONNECTS 128
#define NUM_MSGS 64
/*! \brief Welcome message when starting a CLI interface */
ast_verbose("Asterisk " ASTERISK_VERSION ", Copyright (C) 1999 - 2006 Digium, Inc. and others.\n"); \
ast_verbose("Created by Mark Spencer <markster@digium.com>\n"); \
ast_verbose("Asterisk comes with ABSOLUTELY NO WARRANTY; type 'show warranty' for details.\n"); \
ast_verbose("This is free software, with components licensed under the GNU General Public\n"); \
ast_verbose("License version 2 and other licenses; you are welcome to redistribute it under\n"); \
ast_verbose("certain conditions. Type 'show license' for details.\n"); \
ast_verbose("=========================================================================\n")
/*! \defgroup main_options
\brief Main configuration options from \ref Config_ast "asterisk.conf" or
the operating system command line when starting Asterisk
Some of them can be changed in the CLI
*/
/*! @{ */
Russell Bryant
committed
extern int ast_language_is_prefix; /* XXX move to some header */
struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
Russell Bryant
committed
int option_verbose = 0; /*!< Verbosity level */
int option_debug = 0; /*!< Debug level */
double option_maxload = 0.0; /*!< Max load avg on system */
Russell Bryant
committed
int option_maxcalls = 0; /*!< Max number of active calls */
char record_cache_dir[AST_CACHE_DIR_LEN] = AST_TMP_DIR;
char debug_filename[AST_FILENAME_MAX] = "";
static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
pid_t ast_mainpid;
int fd; /*!< File descriptor */
int p[2]; /*!< Pipe */
pthread_t t; /*!< Thread of handler */
Joshua Colp
committed
int mute; /*!< Is the console muted for logs */
Russell Bryant
committed
struct ast_atexit {
void (*func)(void);
Russell Bryant
committed
AST_LIST_ENTRY(ast_atexit) list;
};
Russell Bryant
committed
static AST_LIST_HEAD_STATIC(atexits, ast_atexit);
static History *el_hist = NULL;
static EditLine *el = NULL;
static char *remotehostname;
char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
static int ast_el_add_history(char *);
static int ast_el_read_history(char *);
static int ast_el_write_history(char *);
char ast_config_AST_CONFIG_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_CONFIG_FILE[AST_CONFIG_MAX_PATH];
char ast_config_AST_MODULE_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_SPOOL_DIR[AST_CONFIG_MAX_PATH];
Kevin P. Fleming
committed
char ast_config_AST_MONITOR_DIR[AST_CONFIG_MAX_PATH];
Joshua Colp
committed
char ast_config_AST_DATA_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_LOG_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_AGI_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_DB[AST_CONFIG_MAX_PATH];
char ast_config_AST_KEY_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_PID[AST_CONFIG_MAX_PATH];
char ast_config_AST_SOCKET[AST_CONFIG_MAX_PATH];
char ast_config_AST_RUN_DIR[AST_CONFIG_MAX_PATH];
char ast_config_AST_RUN_USER[AST_CONFIG_MAX_PATH];
char ast_config_AST_RUN_GROUP[AST_CONFIG_MAX_PATH];
char ast_config_AST_CTL_PERMISSIONS[AST_CONFIG_MAX_PATH];
char ast_config_AST_CTL_OWNER[AST_CONFIG_MAX_PATH] = "\0";
char ast_config_AST_CTL_GROUP[AST_CONFIG_MAX_PATH] = "\0";
char ast_config_AST_CTL[AST_CONFIG_MAX_PATH] = "asterisk.ctl";
Olle Johansson
committed
char ast_config_AST_SYSTEM_NAME[20] = "";
Russell Bryant
committed
extern const char *ast_build_hostname;
extern const char *ast_build_kernel;
extern const char *ast_build_machine;
extern const char *ast_build_os;
extern const char *ast_build_date;
extern const char *ast_build_user;
static char *_argv[256];
static int shuttingdown = 0;
static int restartnow = 0;
static pthread_t consolethread = AST_PTHREADT_NULL;
#if !defined(LOW_MEMORY)
struct file_version {
AST_LIST_ENTRY(file_version) list;
Kevin P. Fleming
committed
const char *file;
char *version;
};
static AST_LIST_HEAD_STATIC(file_versions, file_version);
void ast_register_file_version(const char *file, const char *version)
{
struct file_version *new;
Kevin P. Fleming
committed
char *work;
size_t version_length;
Kevin P. Fleming
committed
work = ast_strdupa(version);
work = ast_strip(ast_strip_quoted(work, "$", "$"));
version_length = strlen(work) + 1;
if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
return;
new->file = file;
Kevin P. Fleming
committed
new->version = (char *) new + sizeof(*new);
memcpy(new->version, work, version_length);
AST_LIST_LOCK(&file_versions);
AST_LIST_INSERT_HEAD(&file_versions, new, list);
AST_LIST_UNLOCK(&file_versions);
}
void ast_unregister_file_version(const char *file)
{
struct file_version *find;
AST_LIST_LOCK(&file_versions);
AST_LIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
if (!strcasecmp(find->file, file)) {
AST_LIST_REMOVE_CURRENT(&file_versions, list);
break;
}
}
AST_LIST_TRAVERSE_SAFE_END;
AST_LIST_UNLOCK(&file_versions);
Kevin P. Fleming
committed
if (find)
free(find);
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
struct thread_list_t {
AST_LIST_ENTRY(thread_list_t) list;
char *name;
pthread_t id;
};
static AST_LIST_HEAD_STATIC(thread_list, thread_list_t);
static char show_threads_help[] =
"Usage: show threads\n"
" List threads currently active in the system.\n";
void ast_register_thread(char *name)
{
struct thread_list_t *new = ast_calloc(1, sizeof(*new));
if (!new)
return;
new->id = pthread_self();
new->name = name; /* this was a copy already */
AST_LIST_LOCK(&thread_list);
AST_LIST_INSERT_HEAD(&thread_list, new, list);
AST_LIST_UNLOCK(&thread_list);
}
void ast_unregister_thread(void *id)
{
struct thread_list_t *x;
AST_LIST_LOCK(&thread_list);
AST_LIST_TRAVERSE_SAFE_BEGIN(&thread_list, x, list) {
if ((void *)x->id == id) {
AST_LIST_REMOVE_CURRENT(&thread_list, list);
break;
}
}
AST_LIST_TRAVERSE_SAFE_END;
AST_LIST_UNLOCK(&thread_list);
if (x) {
free(x->name);
free(x);
}
}
static int handle_show_threads(int fd, int argc, char *argv[])
{
int count = 0;
struct thread_list_t *cur;
AST_LIST_LOCK(&thread_list);
AST_LIST_TRAVERSE(&thread_list, cur, list) {
ast_cli(fd, "%p %s\n", (void *)cur->id, cur->name);
count++;
}
AST_LIST_UNLOCK(&thread_list);
ast_cli(fd, "%d threads listed.\n", count);
return 0;
}
struct profile_entry {
const char *name;
uint64_t scale; /* if non-zero, values are scaled by this */
int64_t mark;
int64_t value;
int64_t events;
};
struct profile_data {
int entries;
int max_size;
struct profile_entry e[0];
};
static struct profile_data *prof_data;
/*
* allocates a counter with a given name and scale.
* Returns the identifier of the counter.
*/
int ast_add_profile(const char *name, uint64_t scale)
{
int l = sizeof(struct profile_data);
int n = 10; /* default entries */
if (prof_data == NULL) {
prof_data = ast_calloc(1, l + n*sizeof(struct profile_entry));
if (prof_data == NULL)
return -1;
prof_data->entries = 0;
prof_data->max_size = n;
}
if (prof_data->entries >= prof_data->max_size) {
void *p;
n = prof_data->max_size + 20;
p = ast_realloc(prof_data, l + n*sizeof(struct profile_entry));
if (p == NULL)
return -1;
prof_data = p;
prof_data->max_size = n;
}
n = prof_data->entries++;
prof_data->e[n].name = ast_strdup(name);
prof_data->e[n].value = 0;
prof_data->e[n].events = 0;
prof_data->e[n].mark = 0;
prof_data->e[n].scale = scale;
return n;
}
int64_t ast_profile(int i, int64_t delta)
{
if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
return 0;
if (prof_data->e[i].scale > 1)
delta /= prof_data->e[i].scale;
prof_data->e[i].value += delta;
prof_data->e[i].events++;
return prof_data->e[i].value;
}
#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
#if defined(__FreeBSD__)
#include <machine/cpufunc.h>
#elif defined(linux)
static __inline u_int64_t
rdtsc(void)
{
uint64_t rv;
__asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
return (rv);
}
#endif
#else /* supply a dummy function on other platforms */
static __inline u_int64_t
rdtsc(void)
{
return 0;
}
#endif
int64_t ast_mark(int i, int startstop)
{
if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
return 0;
if (startstop == 1)
prof_data->e[i].mark = rdtsc();
else {
prof_data->e[i].mark = (rdtsc() - prof_data->e[i].mark);
if (prof_data->e[i].scale > 1)
prof_data->e[i].mark /= prof_data->e[i].scale;
prof_data->e[i].value += prof_data->e[i].mark;
prof_data->e[i].events++;
}
return prof_data->e[i].mark;
}
static int handle_show_profile(int fd, int argc, char *argv[])
{
int i, min, max;
char *search = NULL;
if (prof_data == NULL)
return 0;
min = 0;
max = prof_data->entries;
if (argc >= 3) { /* specific entries */
if (isdigit(argv[2][0])) {
min = atoi(argv[2]);
if (argc == 4 && strcmp(argv[3], "-"))
max = atoi(argv[3]);
} else
search = argv[2];
}
if (max > prof_data->entries)
max = prof_data->entries;
if (!strcmp(argv[0], "clear")) {
for (i= min; i < max; i++) {
if (!search || strstr(prof_data->e[i].name, search)) {
prof_data->e[i].value = 0;
prof_data->e[i].events = 0;
}
}
return 0;
}
ast_cli(fd, "profile values (%d, allocated %d)\n-------------------\n",
prof_data->entries, prof_data->max_size);
for (i = min; i < max; i++) {
struct profile_entry *e = &prof_data->e[i];
if (!search || strstr(prof_data->e[i].name, search))
ast_cli(fd, "%6d: [%8ld] %10ld %12lld %12lld %s\n",
i,
(long)e->scale,
(long)e->events, (long long)e->value,
(long long)(e->events ? e->value / e->events : e->value),
e->name);
}
return 0;
}
static char show_version_files_help[] =
"Usage: show version files [like <pattern>]\n"
" Shows the revision numbers of the files used to build this copy of Asterisk.\n"
" Optional regular expression pattern is used to filter the file list.\n";
/*! CLI command to list module versions */
static int handle_show_version_files(int fd, int argc, char *argv[])
{
#define FORMAT "%-25.25s %-40.40s\n"
struct file_version *iterator;
Kevin P. Fleming
committed
regex_t regexbuf;
int havepattern = 0;
Kevin P. Fleming
committed
int count_files = 0;
Kevin P. Fleming
committed
switch (argc) {
case 5:
if (!strcasecmp(argv[3], "like")) {
if (regcomp(®exbuf, argv[4], REG_EXTENDED | REG_NOSUB))
return RESULT_SHOWUSAGE;
havepattern = 1;
} else
return RESULT_SHOWUSAGE;
break;
case 4:
havename = 1;
break;
Kevin P. Fleming
committed
case 3:
break;
default:
return RESULT_SHOWUSAGE;
}
Kevin P. Fleming
committed
ast_cli(fd, FORMAT, "File", "Revision");
ast_cli(fd, FORMAT, "----", "--------");
AST_LIST_LOCK(&file_versions);
AST_LIST_TRAVERSE(&file_versions, iterator, list) {
if (havename && strcasecmp(iterator->file, argv[3]))
continue;
Kevin P. Fleming
committed
if (havepattern && regexec(®exbuf, iterator->file, 0, NULL, 0))
continue;
ast_cli(fd, FORMAT, iterator->file, iterator->version);
Kevin P. Fleming
committed
count_files++;
}
AST_LIST_UNLOCK(&file_versions);
Kevin P. Fleming
committed
if (!havename) {
ast_cli(fd, "%d files listed.\n", count_files);
}
Kevin P. Fleming
committed
if (havepattern)
regfree(®exbuf);
Kevin P. Fleming
committed
#undef FORMAT
Russell Bryant
committed
static char *complete_show_version_files(const char *line, const char *word, int pos, int state)
{
struct file_version *find;
int which = 0;
char *ret = NULL;
int matchlen = strlen(word);
if (pos != 3)
return NULL;
AST_LIST_LOCK(&file_versions);
AST_LIST_TRAVERSE(&file_versions, find, list) {
if (!strncasecmp(word, find->file, matchlen) && ++which > state) {
ret = ast_strdup(find->file);
break;
}
}
AST_LIST_UNLOCK(&file_versions);
return ret;
}
#endif /* ! LOW_MEMORY */
int ast_register_atexit(void (*func)(void))
{
int res = -1;
struct ast_atexit *ae;
ast_unregister_atexit(func);
Russell Bryant
committed
AST_LIST_LOCK(&atexits);
if ((ae = ast_calloc(1, sizeof(*ae)))) {
Russell Bryant
committed
AST_LIST_INSERT_HEAD(&atexits, ae, list);
ae->func = func;
res = 0;
}
Russell Bryant
committed
AST_LIST_UNLOCK(&atexits);
return res;
}
void ast_unregister_atexit(void (*func)(void))
{
Russell Bryant
committed
struct ast_atexit *ae;
AST_LIST_LOCK(&atexits);
AST_LIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
if (ae->func == func) {
Russell Bryant
committed
AST_LIST_REMOVE_CURRENT(&atexits, list);
break;
}
}
Russell Bryant
committed
AST_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK(&atexits);
}
static int fdprint(int fd, const char *s)
{
return write(fd, s, strlen(s) + 1);
}
/*! NULL handler so we can collect the child exit status */
static void null_sig_handler(int signal)
{
}
AST_MUTEX_DEFINE_STATIC(safe_system_lock);
Russell Bryant
committed
/*! Keep track of how many threads are currently trying to wait*() on
* a child process */
static unsigned int safe_system_level = 0;
static void *safe_system_prev_handler;
Russell Bryant
committed
void ast_replace_sigchld(void)
Mark Spencer
committed
{
unsigned int level;
ast_mutex_lock(&safe_system_lock);
level = safe_system_level++;
/* only replace the handler if it has not already been done */
if (level == 0)
safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
ast_mutex_unlock(&safe_system_lock);
Russell Bryant
committed
}
void ast_unreplace_sigchld(void)
{
unsigned int level;
ast_mutex_lock(&safe_system_lock);
level = --safe_system_level;
/* only restore the handler if we are the last one */
if (level == 0)
signal(SIGCHLD, safe_system_prev_handler);
ast_mutex_unlock(&safe_system_lock);
}
int ast_safe_system(const char *s)
{
pid_t pid;
int x;
int res;
struct rusage rusage;
int status;
ast_replace_sigchld();
Mark Spencer
committed
pid = fork();
Mark Spencer
committed
if (pid == 0) {
if (ast_opt_high_priority)
ast_set_priority(0);
Mark Spencer
committed
/* Close file descriptors and launch system command */
for (x = STDERR_FILENO + 1; x < 4096; x++)
Mark Spencer
committed
close(x);
execl("/bin/sh", "/bin/sh", "-c", s, NULL);
Mark Spencer
committed
} else if (pid > 0) {
for(;;) {
res = wait4(pid, &status, 0, &rusage);
if (res > -1) {
res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
break;
} else if (errno != EINTR)
Mark Spencer
committed
}
} else {
ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
res = -1;
}
Russell Bryant
committed
ast_unreplace_sigchld();
Mark Spencer
committed
return res;
}
Joshua Colp
committed
/*!
* mute or unmute a console from logging
*/
void ast_console_toggle_mute(int fd) {
Joshua Colp
committed
int x;
for (x = 0;x < AST_MAX_CONNECTS; x++) {
Joshua Colp
committed
if (fd == consoles[x].fd) {
if (consoles[x].mute) {
consoles[x].mute = 0;
Joshua Colp
committed
ast_cli(fd, "Console is not muted anymore.\n");
} else {
consoles[x].mute = 1;
Joshua Colp
committed
ast_cli(fd, "Console is muted.\n");
}
return;
}
}
ast_cli(fd, "Couldn't find remote console.\n");
}
/*!
* log the string to all attached console clients
*/
static void ast_network_puts_mutable(const char *string)
{
int x;
for (x = 0;x < AST_MAX_CONNECTS; x++) {
Joshua Colp
committed
if (consoles[x].mute)
continue;
Joshua Colp
committed
if (consoles[x].fd > -1)
fdprint(consoles[x].p[1], string);
}
}
/*!
* log the string to the console, and all attached
* console clients
*/
void ast_console_puts_mutable(const char *string)
{
fputs(string, stdout);
fflush(stdout);
ast_network_puts_mutable(string);
}
* write the string to all attached console clients
*/
static void ast_network_puts(const char *string)
{
Olle Johansson
committed
for (x=0; x < AST_MAX_CONNECTS; x++) {
if (consoles[x].fd > -1)
fdprint(consoles[x].p[1], string);
}
* write the string to the console, and all attached
* console clients
*/
void ast_console_puts(const char *string)
{
fputs(string, stdout);
fflush(stdout);
ast_network_puts(string);
static void network_verboser(const char *s, int pos, int replace, int complete)
char *t;
if ((t = alloca(strlen(s) + 2))) {
Joshua Colp
committed
ast_network_puts_mutable(t);
} else {
ast_log(LOG_ERROR, "Out of memory\n");
Joshua Colp
committed
ast_network_puts_mutable(s);
Joshua Colp
committed
ast_network_puts_mutable(s);
}
static pthread_t lthread;
static void *netconsole(void *vconsole)
{
struct console *con = vconsole;
Olle Johansson
committed
char hostname[MAXHOSTNAMELEN] = "";
struct pollfd fds[2];
if (gethostname(hostname, sizeof(hostname)-1))
ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
snprintf(tmp, sizeof(tmp), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ASTERISK_VERSION);
fds[0].fd = con->fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
fds[1].fd = con->p[0];
fds[1].events = POLLIN;
fds[1].revents = 0;
res = poll(fds, 2, -1);
if (errno != EINTR)
ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
if (fds[0].revents) {
tmp[res] = 0;
ast_cli_command(con->fd, tmp);
}
if (fds[1].revents) {
res = read(con->p[0], tmp, sizeof(tmp));
if (res < 1) {
ast_log(LOG_ERROR, "read returned %d\n", res);
break;
}
res = write(con->fd, tmp, res);
if (res < 1)
break;
}
}
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection disconnected\n");
close(con->fd);
close(con->p[0]);
close(con->p[1]);
con->fd = -1;
return NULL;
}
static void *listener(void *unused)
{
struct sockaddr_un sunaddr;
socklen_t len;
struct pollfd fds[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Olle Johansson
committed
for (;;) {
fds[0].fd = ast_socket;
Olle Johansson
committed
fds[0].events = POLLIN;
s = poll(fds, 1, -1);
ast_log(LOG_WARNING, "poll returned error: %s\n", strerror(errno));
len = sizeof(sunaddr);
s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
Olle Johansson
committed
for (x = 0; x < AST_MAX_CONNECTS; x++) {
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
consoles[x].fd = -1;
fdprint(s, "Server failed to create pipe\n");
close(s);
break;
}
flags = fcntl(consoles[x].p[1], F_GETFL);
fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
consoles[x].mute = ast_opt_mute;
if (ast_pthread_create(&consoles[x].t, &attr, netconsole, &consoles[x])) {
ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
close(consoles[x].p[0]);
close(consoles[x].p[1]);
consoles[x].fd = -1;
fdprint(s, "Server failed to spawn thread\n");
close(s);
}
break;
}
}
if (x >= AST_MAX_CONNECTS) {
fdprint(s, "No more connections allowed\n");
ast_log(LOG_WARNING, "No more connections allowed\n");
close(s);
} else if (consoles[x].fd > -1) {
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection\n");
}
}
}
return NULL;
}
static int ast_makesocket(void)
{
struct sockaddr_un sunaddr;
uid_t uid = -1;
gid_t gid = -1;
for (x = 0; x < AST_MAX_CONNECTS; x++)
unlink(ast_config_AST_SOCKET);
ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
if (ast_socket < 0) {
ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
return -1;
}
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_LOCAL;
ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
res = bind(ast_socket, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
close(ast_socket);
ast_socket = -1;
return -1;
}
res = listen(ast_socket, 2);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
close(ast_socket);
ast_socket = -1;
return -1;
}
ast_register_verbose(network_verboser);
ast_pthread_create(<hread, NULL, listener, NULL);
if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL) {
ast_log(LOG_WARNING, "Unable to find uid of user %s\n", ast_config_AST_CTL_OWNER);
} else {
uid = pw->pw_uid;
}
if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL) {
ast_log(LOG_WARNING, "Unable to find gid of group %s\n", ast_config_AST_CTL_GROUP);
} else {
gid = grp->gr_gid;
}
if (chown(ast_config_AST_SOCKET, uid, gid) < 0)
ast_log(LOG_WARNING, "Unable to change ownership of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
if (!ast_strlen_zero(ast_config_AST_CTL_PERMISSIONS)) {
sscanf(ast_config_AST_CTL_PERMISSIONS, "%o", &p1);
p = p1;
if ((chmod(ast_config_AST_SOCKET, p)) < 0)
ast_log(LOG_WARNING, "Unable to change file permissions of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
return 0;
}
static int ast_tryconnect(void)
{
struct sockaddr_un sunaddr;
int res;
ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (ast_consock < 0) {
ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
return 0;
}
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_LOCAL;
ast_copy_string(sunaddr.sun_path, (char *)ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
res = connect(ast_consock, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
if (res) {
close(ast_consock);
ast_consock = -1;
return 0;
} else
return 1;
}
/*! Urgent handler
Called by soft_hangup to interrupt the poll, read, or other
system call. We don't actually need to do anything though.
Remember: Cannot EVER ast_log from within a signal handler
SLD: seems to be some pthread activity relating to the printf anyway:
which is leading to a deadlock?
*/
Kevin P. Fleming
committed
#if 0
if (option_debug > 2)
printf("-- Asterisk Urgent handler\n");
Kevin P. Fleming
committed
#endif