Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:55 2009

## Asterisk developer's documentation

# res_monitor.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief PBX channel monitoring
    00022  *
    00023  */
    00024
    00025 #include <stdio.h>
    00026 #include <stdlib.h>
    00027 #include <errno.h>
    00028 #include <string.h>
    00029 #include <sys/types.h>
    00030 #include <sys/stat.h>
    00031 #include <libgen.h>     /* dirname() */
    00032
    00033 #include "asterisk.h"
    00034
    00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 59086 $")
    00036
    00037 #include "asterisk/lock.h"
    00038 #include "asterisk/channel.h"
    00039 #include "asterisk/logger.h"
    00040 #include "asterisk/file.h"
    00041 #include "asterisk/pbx.h"
    00042 #include "asterisk/module.h"
    00043 #include "asterisk/manager.h"
    00044 #include "asterisk/cli.h"
    00045 #include "asterisk/monitor.h"
    00046 #include "asterisk/app.h"
    00047 #include "asterisk/utils.h"
    00048 #include "asterisk/config.h"
    00049
    00050 AST_MUTEX_DEFINE_STATIC(monitorlock);
    00051
    00052 static unsigned long seq = 0;
    00053
    00054 static char *monitor_synopsis = "Monitor a channel";
    00055
    00056 static char *monitor_descrip = "Monitor([file_format[:urlbase]|[fname_base]|[options]]):\n"
    00057 "Used to start monitoring a channel. The channel's input and output\n"
    00058 "voice packets are logged to files until the channel hangs up or\n"
    00059 "monitoring is stopped by the StopMonitor application.\n"
    00060 "  file_format    optional, if not set, defaults to \"wav\"\n"
    00061 "  fname_base     if set, changes the filename used to the one specified.\n"
    00062 "  options:\n"
    00063 "    m   - when the recording ends mix the two leg files into one and\n"
    00064 "          delete the two leg files.  If the variable MONITOR_EXEC is set, the\n"
    00065 "          application referenced in it will be executed instead of\n"
    00066 "          soxmix and the raw leg files will NOT be deleted automatically.\n"
    00067 "          soxmix or MONITOR_EXEC is handed 3 arguments, the two leg files\n"
    00068 "          and a target mixed file name which is the same as the leg file names\n"
    00069 "          only without the in/out designator.\n"
    00070 "          If MONITOR_EXEC_ARGS is set, the contents will be passed on as\n"
    00071 "          additional arguements to MONITOR_EXEC\n"
    00072 "          Both MONITOR_EXEC and the Mix flag can be set from the\n"
    00073 "          administrator interface\n"
    00074 "\n"
    00075 "    b   - Don't begin recording unless a call is bridged to another channel\n"
    00076 "\nReturns -1 if monitor files can't be opened or if the channel is already\n"
    00077 "monitored, otherwise 0.\n"
    00078 ;
    00079
    00080 static char *stopmonitor_synopsis = "Stop monitoring a channel";
    00081
    00082 static char *stopmonitor_descrip = "StopMonitor\n"
    00083    "Stops monitoring a channel. Has no effect if the channel is not monitored\n";
    00084
    00085 static char *changemonitor_synopsis = "Change monitoring filename of a channel";
    00086
    00087 static char *changemonitor_descrip = "ChangeMonitor(filename_base)\n"
    00088    "Changes monitoring filename of a channel. Has no effect if the channel is not monitored\n"
    00089    "The argument is the new filename base to use for monitoring this channel.\n";
    00090
    00091 /* Start monitoring a channel */
    00092 int ast_monitor_start(  struct ast_channel *chan, const char *format_spec,
    00093       const char *fname_base, int need_lock)
    00094 {
    00095    int res = 0;
    00096    char tmp[256];
    00097
    00098    if (need_lock) {
    00099       if (ast_mutex_lock(&chan->lock)) {
    00100          ast_log(LOG_WARNING, "Unable to lock channel\n");
    00101          return -1;
    00102       }
    00103    }
    00104
    00105    if (!(chan->monitor)) {
    00106       struct ast_channel_monitor *monitor;
    00107       char *channel_name, *p;
    00108
    00109       /* Create monitoring directory if needed */
    00110       if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) {
    00111          if (errno != EEXIST) {
    00112             ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
    00113                strerror(errno));
    00114          }
    00115       }
    00116
    00117       monitor = malloc(sizeof(struct ast_channel_monitor));
    00118       if (!monitor) {
    00119          if (need_lock)
    00120             ast_mutex_unlock(&chan->lock);
    00121          return -1;
    00122       }
    00123       memset(monitor, 0, sizeof(struct ast_channel_monitor));
    00124
    00125       /* Determine file names */
    00126       if (!ast_strlen_zero(fname_base)) {
    00127          int directory = strchr(fname_base, '/') ? 1 : 0;
    00128          /* try creating the directory just in case it doesn't exist */
    00129          if (directory) {
    00130             char *name = strdup(fname_base);
    00131             snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name));
    00132             free(name);
    00133             ast_safe_system(tmp);
    00134          }
    00135          snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
    00136                   directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
    00137          snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
    00138                   directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
    00139          ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
    00140       } else {
    00141          ast_mutex_lock(&monitorlock);
    00142          snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
    00143                   ast_config_AST_MONITOR_DIR, seq);
    00144          snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
    00145                   ast_config_AST_MONITOR_DIR, seq);
    00146          seq++;
    00147          ast_mutex_unlock(&monitorlock);
    00148
    00149          if((channel_name = ast_strdupa(chan->name))) {
    00150             while((p = strchr(channel_name, '/'))) {
    00151                *p = '-';
    00152             }
    00153             snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
    00154                    ast_config_AST_MONITOR_DIR, (int)time(NULL),channel_name);
    00155             monitor->filename_changed = 1;
    00156          } else {
    00157             ast_log(LOG_ERROR,"Failed to allocate Memory\n");
    00158             return -1;
    00159          }
    00160       }
    00161
    00162       monitor->stop = ast_monitor_stop;
    00163
    00164       /* Determine file format */
    00165       if (!ast_strlen_zero(format_spec)) {
    00166          monitor->format = strdup(format_spec);
    00167       } else {
    00168          monitor->format = strdup("wav");
    00169       }
    00170
    00171       /* open files */
    00172       if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) {
    00173          ast_filedelete(monitor->read_filename, NULL);
    00174       }
    00175       if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
    00176                   monitor->format, NULL,
    00177                   O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
    00178          ast_log(LOG_WARNING, "Could not create file %s\n",
    00179                   monitor->read_filename);
    00180          free(monitor);
    00181          ast_mutex_unlock(&chan->lock);
    00182          return -1;
    00183       }
    00184       if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
    00185          ast_filedelete(monitor->write_filename, NULL);
    00186       }
    00187       if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
    00188                   monitor->format, NULL,
    00189                   O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
    00190          ast_log(LOG_WARNING, "Could not create file %s\n",
    00191                   monitor->write_filename);
    00192          ast_closestream(monitor->read_stream);
    00193          free(monitor);
    00194          ast_mutex_unlock(&chan->lock);
    00195          return -1;
    00196       }
    00197       chan->monitor = monitor;
    00198       /* so we know this call has been monitored in case we need to bill for it or something */
    00199       pbx_builtin_setvar_helper(chan, "__MONITORED","true");
    00200    } else {
    00201       ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
    00202                chan->name);
    00203       res = -1;
    00204    }
    00205
    00206    if (need_lock) {
    00207       ast_mutex_unlock(&chan->lock);
    00208    }
    00209    return res;
    00210 }
    00211
    00212 /*
    00213  * The file format extensions that Asterisk uses are not all the same as that
    00214  * which soxmix expects.  This function ensures that the format used as the
    00215  * extension on the filename is something soxmix will understand.
    00216  */
    00217 static const char *get_soxmix_format(const char *format)
    00218 {
    00219    const char *res = format;
    00220
    00221    if (!strcasecmp(format,"ulaw"))
    00222       res = "ul";
    00223    if (!strcasecmp(format,"alaw"))
    00224       res = "al";
    00225
    00226    return res;
    00227 }
    00228
    00229 /* Stop monitoring a channel */
    00230 int ast_monitor_stop(struct ast_channel *chan, int need_lock)
    00231 {
    00232    char *execute, *execute_args;
    00233    int delfiles = 0;
    00234
    00235    if (need_lock) {
    00236       if (ast_mutex_lock(&chan->lock)) {
    00237          ast_log(LOG_WARNING, "Unable to lock channel\n");
    00238          return -1;
    00239       }
    00240    }
    00241
    00242    if (chan->monitor) {
    00243       char filename[ FILENAME_MAX ];
    00244
    00245       if (chan->monitor->read_stream) {
    00246          ast_closestream(chan->monitor->read_stream);
    00247       }
    00248       if (chan->monitor->write_stream) {
    00249          ast_closestream(chan->monitor->write_stream);
    00250       }
    00251
    00252       if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
    00253          if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
    00254             snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
    00255             if (ast_fileexists(filename, NULL, NULL) > 0) {
    00256                ast_filedelete(filename, NULL);
    00257             }
    00258             ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
    00259          } else {
    00260             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
    00261          }
    00262
    00263          if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
    00264             snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
    00265             if (ast_fileexists(filename, NULL, NULL) > 0) {
    00266                ast_filedelete(filename, NULL);
    00267             }
    00268             ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
    00269          } else {
    00270             ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
    00271          }
    00272       }
    00273
    00274       if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
    00275          char tmp[1024];
    00276          char tmp2[1024];
    00277          const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
    00278          char *name = chan->monitor->filename_base;
    00279          int directory = strchr(name, '/') ? 1 : 0;
    00280          char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
    00281
    00282          /* Set the execute application */
    00283          execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
    00284          if (ast_strlen_zero(execute)) {
    00285             execute = "nice -n 19 soxmix";
    00286             format = get_soxmix_format(format);
    00287             delfiles = 1;
    00288          }
    00289          execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
    00290          if (ast_strlen_zero(execute_args)) {
    00291             execute_args = "";
    00292          }
    00293
    00294          snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
    00295          if (delfiles) {
    00296             snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
    00297             ast_copy_string(tmp, tmp2, sizeof(tmp));
    00298          }
    00299          ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
    00300          if (ast_safe_system(tmp) == -1)
    00301             ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
    00302       }
    00303
    00304       free(chan->monitor->format);
    00305       free(chan->monitor);
    00306       chan->monitor = NULL;
    00307    }
    00308
    00309    if (need_lock)
    00310       ast_mutex_unlock(&chan->lock);
    00311    return 0;
    00312 }
    00313
    00314 /* Change monitoring filename of a channel */
    00315 int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
    00316 {
    00317    char tmp[256];
    00318    if (ast_strlen_zero(fname_base)) {
    00319       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
    00320       return -1;
    00321    }
    00322
    00323    if (need_lock) {
    00324       if (ast_mutex_lock(&chan->lock)) {
    00325          ast_log(LOG_WARNING, "Unable to lock channel\n");
    00326          return -1;
    00327       }
    00328    }
    00329
    00330    if (chan->monitor) {
    00331       int directory = strchr(fname_base, '/') ? 1 : 0;
    00332       /* try creating the directory just in case it doesn't exist */
    00333       if (directory) {
    00334          char *name = strdup(fname_base);
    00335          snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name));
    00336          free(name);
    00337          ast_safe_system(tmp);
    00338       }
    00339
    00340       snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
    00341       chan->monitor->filename_changed = 1;
    00342    } else {
    00343       ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
    00344    }
    00345
    00346    if (need_lock)
    00347       ast_mutex_unlock(&chan->lock);
    00348
    00349    return 0;
    00350 }
    00351
    00352 static int start_monitor_exec(struct ast_channel *chan, void *data)
    00353 {
    00354    char *arg = NULL;
    00355    char *format = NULL;
    00356    char *fname_base = NULL;
    00357    char *options = NULL;
    00358    char *delay = NULL;
    00359    char *urlprefix = NULL;
    00360    char tmp[256];
    00361    int joinfiles = 0;
    00362    int waitforbridge = 0;
    00363    int res = 0;
    00364
    00365    /* Parse arguments. */
    00366    if (!ast_strlen_zero((char*)data)) {
    00367       arg = ast_strdupa((char*)data);
    00368       format = arg;
    00369       fname_base = strchr(arg, '|');
    00370       if (fname_base) {
    00371          *fname_base = 0;
    00372          fname_base++;
    00373          if ((options = strchr(fname_base, '|'))) {
    00374             *options = 0;
    00375             options++;
    00376             if (strchr(options, 'm'))
    00377                joinfiles = 1;
    00378             if (strchr(options, 'b'))
    00379                waitforbridge = 1;
    00380          }
    00381       }
    00382       arg = strchr(format,':');
    00383       if (arg) {
    00384          *arg++ = 0;
    00385          urlprefix = arg;
    00386       }
    00387    }
    00388    if (urlprefix) {
    00389       snprintf(tmp,sizeof(tmp) - 1,"%s/%s.%s",urlprefix,fname_base,
    00390          ((strcmp(format,"gsm")) ? "wav" : "gsm"));
    00391       if (!chan->cdr)
    00392          chan->cdr = ast_cdr_alloc();
    00393       ast_cdr_setuserfield(chan, tmp);
    00394    }
    00395    if (waitforbridge) {
    00396       /* We must remove the "b" option if listed.  In principle none of
    00397          the following could give NULL results, but we check just to
    00398          be pedantic. Reconstructing with checks for 'm' option does not
    00399          work if we end up adding more options than 'm' in the future. */
    00400       delay = ast_strdupa((char*)data);
    00401       if (delay) {
    00402          options = strrchr(delay, '|');
    00403          if (options) {
    00404             arg = strchr(options, 'b');
    00405             if (arg) {
    00406                *arg = 'X';
    00407                pbx_builtin_setvar_helper(chan,"AUTO_MONITOR",delay);
    00408             }
    00409          }
    00410       }
    00411       return 0;
    00412    }
    00413
    00414    res = ast_monitor_start(chan, format, fname_base, 1);
    00415    if (res < 0)
    00416       res = ast_monitor_change_fname(chan, fname_base, 1);
    00417    ast_monitor_setjoinfiles(chan, joinfiles);
    00418
    00419    return res;
    00420 }
    00421
    00422 static int stop_monitor_exec(struct ast_channel *chan, void *data)
    00423 {
    00424    return ast_monitor_stop(chan, 1);
    00425 }
    00426
    00427 static int change_monitor_exec(struct ast_channel *chan, void *data)
    00428 {
    00429    return ast_monitor_change_fname(chan, (const char*)data, 1);
    00430 }
    00431
    00432 static char start_monitor_action_help[] =
    00433 "Description: The 'Monitor' action may be used to record the audio on a\n"
    00434 "  specified channel.  The following parameters may be used to control\n"
    00435 "  this:\n"
    00436 "  Channel     - Required.  Used to specify the channel to record.\n"
    00437 "  File        - Optional.  Is the name of the file created in the\n"
    00438 "                monitor spool directory.  Defaults to the same name\n"
    00439 "                as the channel (with slashes replaced with dashes).\n"
    00440 "  Format      - Optional.  Is the audio recording format.  Defaults\n"
    00441 "                to \"wav\".\n"
    00442 "  Mix         - Optional.  Boolean parameter as to whether to mix\n"
    00443 "                the input and output channels together after the\n"
    00444 "                recording is finished.\n";
    00445
    00446 static int start_monitor_action(struct mansession *s, struct message *m)
    00447 {
    00448    struct ast_channel *c = NULL;
    00449    char *name = astman_get_header(m, "Channel");
    00450    char *fname = astman_get_header(m, "File");
    00451    char *format = astman_get_header(m, "Format");
    00452    char *mix = astman_get_header(m, "Mix");
    00453    char *d;
    00454
    00455    if (ast_strlen_zero(name)) {
    00456       astman_send_error(s, m, "No channel specified");
    00457       return 0;
    00458    }
    00459    c = ast_get_channel_by_name_locked(name);
    00460    if (!c) {
    00461       astman_send_error(s, m, "No such channel");
    00462       return 0;
    00463    }
    00464
    00465    if (ast_strlen_zero(fname)) {
    00466       /* No filename base specified, default to channel name as per CLI */
    00467       fname = malloc (FILENAME_MAX);
    00468       if (!fname) {
    00469          astman_send_error(s, m, "Could not start monitoring channel");
    00470          ast_mutex_unlock(&c->lock);
    00471          return 0;
    00472       }
    00473       memset(fname, 0, FILENAME_MAX);
    00474       ast_copy_string(fname, c->name, FILENAME_MAX);
    00475       /* Channels have the format technology/channel_name - have to replace that /  */
    00476       if ((d=strchr(fname, '/'))) *d='-';
    00477    }
    00478
    00479    if (ast_monitor_start(c, format, fname, 1)) {
    00480       if (ast_monitor_change_fname(c, fname, 1)) {
    00481          astman_send_error(s, m, "Could not start monitoring channel");
    00482          ast_mutex_unlock(&c->lock);
    00483          return 0;
    00484       }
    00485    }
    00486
    00487    if (ast_true(mix)) {
    00488       ast_monitor_setjoinfiles(c, 1);
    00489    }
    00490
    00491    ast_mutex_unlock(&c->lock);
    00492    astman_send_ack(s, m, "Started monitoring channel");
    00493    return 0;
    00494 }
    00495
    00496 static char stop_monitor_action_help[] =
    00497 "Description: The 'StopMonitor' action may be used to end a previously\n"
    00498 "  started 'Monitor' action.  The only parameter is 'Channel', the name\n"
    00499 "  of the channel monitored.\n";
    00500
    00501 static int stop_monitor_action(struct mansession *s, struct message *m)
    00502 {
    00503    struct ast_channel *c = NULL;
    00504    char *name = astman_get_header(m, "Channel");
    00505    int res;
    00506    if (ast_strlen_zero(name)) {
    00507       astman_send_error(s, m, "No channel specified");
    00508       return 0;
    00509    }
    00510    c = ast_get_channel_by_name_locked(name);
    00511    if (!c) {
    00512       astman_send_error(s, m, "No such channel");
    00513       return 0;
    00514    }
    00515    res = ast_monitor_stop(c, 1);
    00516    ast_mutex_unlock(&c->lock);
    00517    if (res) {
    00518       astman_send_error(s, m, "Could not stop monitoring channel");
    00519       return 0;
    00520    }
    00521    astman_send_ack(s, m, "Stopped monitoring channel");
    00522    return 0;
    00523 }
    00524
    00525 static char change_monitor_action_help[] =
    00526 "Description: The 'ChangeMonitor' action may be used to change the file\n"
    00527 "  started by a previous 'Monitor' action.  The following parameters may\n"
    00528 "  be used to control this:\n"
    00529 "  Channel     - Required.  Used to specify the channel to record.\n"
    00530 "  File        - Required.  Is the new name of the file created in the\n"
    00531 "                monitor spool directory.\n";
    00532
    00533 static int change_monitor_action(struct mansession *s, struct message *m)
    00534 {
    00535    struct ast_channel *c = NULL;
    00536    char *name = astman_get_header(m, "Channel");
    00537    char *fname = astman_get_header(m, "File");
    00538    if (ast_strlen_zero(name)) {
    00539       astman_send_error(s, m, "No channel specified");
    00540       return 0;
    00541    }
    00542    if (ast_strlen_zero(fname)) {
    00543       astman_send_error(s, m, "No filename specified");
    00544       return 0;
    00545    }
    00546    c = ast_get_channel_by_name_locked(name);
    00547    if (!c) {
    00548       astman_send_error(s, m, "No such channel");
    00549       return 0;
    00550    }
    00551    if (ast_monitor_change_fname(c, fname, 1)) {
    00552       astman_send_error(s, m, "Could not change monitored filename of channel");
    00553       ast_mutex_unlock(&c->lock);
    00554       return 0;
    00555    }
    00556    ast_mutex_unlock(&c->lock);
    00557    astman_send_ack(s, m, "Changed monitor filename");
    00558    return 0;
    00559 }
    00560
    00561 void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
    00562 {
    00563    if (chan->monitor)
    00564       chan->monitor->joinfiles = turnon;
    00565 }
    00566
    00567 int load_module(void)
    00568 {
    00569    ast_register_application("Monitor", start_monitor_exec, monitor_synopsis, monitor_descrip);
    00570    ast_register_application("StopMonitor", stop_monitor_exec, stopmonitor_synopsis, stopmonitor_descrip);
    00571    ast_register_application("ChangeMonitor", change_monitor_exec, changemonitor_synopsis, changemonitor_descrip);
    00572    ast_manager_register2("Monitor", EVENT_FLAG_CALL, start_monitor_action, monitor_synopsis, start_monitor_action_help);
    00573    ast_manager_register2("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action, stopmonitor_synopsis, stop_monitor_action_help);
    00574    ast_manager_register2("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action, changemonitor_synopsis, change_monitor_action_help);
    00575
    00576    return 0;
    00577 }
    00578
    00579 int unload_module(void)
    00580 {
    00581    ast_unregister_application("Monitor");
    00582    ast_unregister_application("StopMonitor");
    00583    ast_unregister_application("ChangeMonitor");
    00584    ast_manager_unregister("Monitor");
    00585    ast_manager_unregister("StopMonitor");
    00586    ast_manager_unregister("ChangeMonitor");
    00587    return 0;
    00588 }
    00589
    00590 char *description(void)
    00591 {
    00592    return "Call Monitoring Resource";
    00593 }
    00594
    00595 int usecount(void)
    00596 {
    00597    /* Never allow monitor to be unloaded because it will
    00598       unresolve needed symbols in the channel */
    00599 #if 0
    00600    int res;
    00601    STANDARD_USECOUNT(res);
    00602    return res;
    00603 #else
    00604    return 1;
    00605 #endif
    00606 }
    00607
    00608 char *key()
    00609 {
    00610    return ASTERISK_GPL_KEY;
    00611 }