Sat Apr 26 2014 22:01:27

Asterisk developer's documentation


app_skel.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) <Year>, <Your Name Here>
00005  *
00006  * <Your Name Here> <<Your Email Here>>
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  * Please follow coding guidelines
00019  * https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief Skeleton application
00025  *
00026  * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
00027  *
00028  * This is a skeleton for development of an Asterisk application
00029  * \ingroup applications
00030  */
00031 
00032 /*** MODULEINFO
00033    <defaultenabled>no</defaultenabled>
00034    <support_level>core</support_level>
00035  ***/
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 368673 $")
00040 
00041 #include <math.h> /* log10 */
00042 #include "asterisk/file.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/lock.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/config_options.h"
00050 #include "asterisk/say.h"
00051 #include "asterisk/astobj2.h"
00052 #include "asterisk/acl.h"
00053 #include "asterisk/netsock2.h"
00054 #include "asterisk/strings.h"
00055 #include "asterisk/cli.h"
00056 
00057 /*** DOCUMENTATION
00058    <application name="SkelGuessNumber" language="en_US">
00059       <synopsis>
00060          An example number guessing game
00061       </synopsis>
00062       <syntax>
00063          <parameter name="level" required="true"/>
00064          <parameter name="options">
00065             <optionlist>
00066                <option name="c">
00067                   <para>The computer should cheat</para>
00068                </option>
00069                <option name="n">
00070                   <para>How many games to play before hanging up</para>
00071                </option>
00072             </optionlist>
00073          </parameter>
00074       </syntax>
00075       <description>
00076       <para>This simple number guessing application is a template to build other applications
00077       from. It shows you the basic structure to create your own Asterisk applications.</para>
00078       </description>
00079    </application>
00080  ***/
00081 
00082 static char *app = "SkelGuessNumber";
00083 
00084 enum option_flags {
00085    OPTION_CHEAT    = (1 << 0),
00086    OPTION_NUMGAMES = (1 << 1),
00087 };
00088 
00089 enum option_args {
00090    OPTION_ARG_NUMGAMES,
00091    /* This *must* be the last value in this enum! */
00092    OPTION_ARG_ARRAY_SIZE,
00093 };
00094 
00095 AST_APP_OPTIONS(app_opts,{
00096    AST_APP_OPTION('c', OPTION_CHEAT),
00097    AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
00098 });
00099 
00100 /*! \brief A structure to hold global configuration-related options */
00101 struct skel_global_config {
00102    AST_DECLARE_STRING_FIELDS(
00103       AST_STRING_FIELD(prompt); /*!< The comma-separated list of sounds to prompt to enter a number */
00104       AST_STRING_FIELD(wrong);  /*!< The comma-separated list of sounds to indicate a wrong guess */
00105       AST_STRING_FIELD(right);  /*!< The comma-separated list of sounds to indicate a right guess */
00106       AST_STRING_FIELD(high);   /*!< The comma-separated list of sounds to indicate a high guess */
00107       AST_STRING_FIELD(low);    /*!< The comma-separated list of sounds to indicate a low guess */
00108       AST_STRING_FIELD(lose);  /*!< The comma-separated list of sounds to indicate a lost game */
00109    );
00110    uint32_t num_games;    /*!< The number of games to play before hanging up */
00111    unsigned char cheat:1; /*!< Whether the computer can cheat or not */
00112 };
00113 
00114 /*! \brief A structure to maintain level state across reloads */
00115 struct skel_level_state {
00116    uint32_t wins;      /*!< How many wins for this level */
00117    uint32_t losses;    /*!< How many losses for this level */
00118    double avg_guesses; /*!< The average number of guesses to win for this level */
00119 };
00120 
00121 /*! \brief Object to hold level config information.
00122  * \note This object should hold a reference to an an object that holds state across reloads.
00123  * The other fields are just examples of the kind of data that might be stored in an level.
00124  */
00125 struct skel_level {
00126    AST_DECLARE_STRING_FIELDS(
00127       AST_STRING_FIELD(name);      /*!< The name of the level */
00128    );
00129    uint32_t max_num;                /*!< The upper value on th range of numbers to guess */
00130    uint32_t max_guesses;            /*!< The maximum number of guesses before losing */
00131    struct skel_level_state *state;  /*!< A pointer to level state that must exist across all reloads */
00132 };
00133 
00134 /*! \brief Information about a currently running set of games
00135  * \note Because we want to be able to show true running information about the games
00136  * regardless of whether or not a reload has modified what the level looks like, it
00137  * is important to either copy the information we need from the level to the
00138  * current_game struct, or as we do here, store a reference to the level as it is for
00139  * the running game.
00140  */
00141 struct skel_current_game {
00142    uint32_t total_games;          /*! The total number of games for this call to to the app */
00143    uint32_t games_left;           /*! How many games are left to play in this set */
00144    uint32_t cheat;                /*! Whether or not cheating was enabled for the game */
00145    struct skel_level *level_info; /*! The level information for the running game */
00146 };
00147 
00148 /* Treat the levels as an array--there won't be many and this will maintain the order */
00149 #define LEVEL_BUCKETS 1
00150 
00151 /*! \brief A container that holds all config-related information
00152  * \note This object should contain a pointer to structs for global data and containers for
00153  * any levels that are configured. Objects of this type will be swapped out on reload. If an
00154  * level needs to maintain state across reloads, it needs to allocate a refcounted object to
00155  * hold that state and ensure that a reference is passed to that state when creating a new
00156  * level for reload. */
00157 struct skel_config {
00158    struct skel_global_config *global;
00159    struct ao2_container *levels;
00160 };
00161 
00162 /* Config Options API callbacks */
00163 
00164 /*! \brief Allocate a skel_config to hold a snapshot of the complete results of parsing a config
00165  * \internal
00166  * \returns A void pointer to a newly allocated skel_config
00167  */
00168 static void *skel_config_alloc(void);
00169 
00170 /*! \brief Allocate a skel_level based on a category in a configuration file
00171  * \param cat The category to base the level on
00172  * \returns A void pointer to a newly allocated skel_level
00173  */
00174 static void *skel_level_alloc(const char *cat);
00175 
00176 /*! \brief Find a skel level in the specified container
00177  * \note This function *does not* look for a skel_level in the active container. It is used
00178  * internally by the Config Options code to check if an level has already been added to the
00179  * container that will be swapped for the live container on a successul reload.
00180  *
00181  * \param container A non-active container to search for a level
00182  * \param category The category associated with the level to check for
00183  * \retval non-NULL The level from the container
00184  * \retval NULL The level does not exist in the container
00185  */
00186 static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
00187 
00188 /*! \brief An aco_type structure to link the "general" category to the skel_global_config type */
00189 static struct aco_type global_option = {
00190    .type = ACO_GLOBAL,
00191    .item_offset = offsetof(struct skel_config, global),
00192    .category_match = ACO_WHITELIST,
00193    .category = "^general$",
00194 };
00195 
00196 struct aco_type *global_options[] = ACO_TYPES(&global_option);
00197 
00198 /*! \brief An aco_type structure to link the "sounds" category to the skel_global_config type */
00199 static struct aco_type sound_option = {
00200    .type = ACO_GLOBAL,
00201    .item_offset = offsetof(struct skel_config, global),
00202    .category_match = ACO_WHITELIST,
00203    .category = "^sounds$",
00204 };
00205 
00206 struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
00207 
00208 /*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
00209 static struct aco_type level_option = {
00210    .type = ACO_ITEM,
00211    .category_match = ACO_BLACKLIST,
00212    .category = "^(general|sounds)$",
00213    .item_alloc = skel_level_alloc,
00214    .item_find = skel_level_find,
00215    .item_offset = offsetof(struct skel_config, levels),
00216 };
00217 
00218 struct aco_type *level_options[] = ACO_TYPES(&level_option);
00219 
00220 struct aco_file app_skel_conf = {
00221    .filename = "app_skel.conf",
00222    .types = ACO_TYPES(&global_option, &sound_option, &level_option),
00223 };
00224 
00225 /*! \brief A global object container that will contain the skel_config that gets swapped out on reloads */
00226 static AO2_GLOBAL_OBJ_STATIC(globals);
00227 
00228 /*! \brief The container of active games */
00229 static struct ao2_container *games;
00230 
00231 /*! \brief Register information about the configs being processed by this module */
00232 CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
00233    .files = ACO_FILES(&app_skel_conf),
00234 );
00235 
00236 static void skel_global_config_destructor(void *obj)
00237 {
00238    struct skel_global_config *global = obj;
00239    ast_string_field_free_memory(global);
00240 }
00241 
00242 static void skel_game_destructor(void *obj)
00243 {
00244    struct skel_current_game *game = obj;
00245    ao2_cleanup(game->level_info);
00246 }
00247 
00248 static void skel_state_destructor(void *obj)
00249 {
00250    return;
00251 }
00252 
00253 static struct skel_current_game *skel_game_alloc(struct skel_level *level)
00254 {
00255    struct skel_current_game *game;
00256    if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
00257       return NULL;
00258    }
00259    ao2_ref(level, +1);
00260    game->level_info = level;
00261    return game;
00262 }
00263 
00264 static void skel_level_destructor(void *obj)
00265 {
00266    struct skel_level *level = obj;
00267    ast_string_field_free_memory(level);
00268    ao2_cleanup(level->state);
00269 }
00270 
00271 static int skel_level_hash(const void *obj, const int flags)
00272 {
00273    const struct skel_level *level = obj;
00274    const char *name = (flags & OBJ_KEY) ? obj : level->name;
00275    return ast_str_case_hash(name);
00276 }
00277 
00278 static int skel_level_cmp(void *obj, void *arg, int flags)
00279 {
00280    struct skel_level *one = obj, *two = arg;
00281    const char *match = (flags & OBJ_KEY) ? arg : two->name;
00282    return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
00283 }
00284 
00285 /*! \brief A custom bitfield handler
00286  * \internal
00287  * \note It is not possible to take the address of a bitfield, therefor all
00288  * bitfields in the config struct will have to use a custom handler
00289  * \param opt The opaque config option
00290  * \param var The ast_variable containing the option name and value
00291  * \param obj The object registerd for this option type
00292  * \retval 0 Success
00293  * \retval non-zero Failure
00294  */
00295 static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
00296 {
00297    struct skel_global_config *global = obj;
00298 
00299    if (!strcasecmp(var->name, "cheat")) {
00300       global->cheat = ast_true(var->value);
00301    } else {
00302       return -1;
00303    }
00304 
00305    return 0;
00306 }
00307 
00308 static void play_files_helper(struct ast_channel *chan, const char *prompts)
00309 {
00310    char *prompt, *rest = ast_strdupa(prompts);
00311 
00312    ast_stopstream(chan);
00313    while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
00314       ast_stopstream(chan);
00315    }
00316 }
00317 
00318 static int app_exec(struct ast_channel *chan, const char *data)
00319 {
00320    int win = 0;
00321    uint32_t guesses;
00322    RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00323    RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
00324    RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
00325    char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
00326    struct ast_flags flags;
00327    AST_DECLARE_APP_ARGS(args,
00328       AST_APP_ARG(level);
00329       AST_APP_ARG(options);
00330    );
00331 
00332    if (!cfg) {
00333       ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
00334       return -1;
00335    }
00336 
00337    if (ast_strlen_zero(data)) {
00338       ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
00339       return -1;
00340    }
00341 
00342    /* We need to make a copy of the input string if we are going to modify it! */
00343    parse = ast_strdupa(data);
00344 
00345    AST_STANDARD_APP_ARGS(args, parse);
00346 
00347    if (args.argc == 2) {
00348       ast_app_parse_options(app_opts, &flags, opts, args.options);
00349    }
00350 
00351    if (ast_strlen_zero(args.level)) {
00352       ast_log(LOG_ERROR, "%s requires a level argument\n", app);
00353       return -1;
00354    }
00355 
00356    if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
00357       ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
00358       return -1;
00359    }
00360 
00361    if (!(game = skel_game_alloc(level))) {
00362       return -1;
00363    }
00364 
00365    ao2_link(games, game);
00366 
00367    /* Use app-specified values, or the options specified in [general] if they aren't passed to the app */
00368    if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
00369          ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
00370          ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
00371       game->total_games = cfg->global->num_games;
00372    }
00373    game->games_left = game->total_games;
00374    game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
00375 
00376    for (game->games_left = game->total_games; game->games_left; game->games_left--) {
00377       uint32_t num = ast_random() % level->max_num; /* random number between 0 and level->max_num */
00378 
00379       ast_debug(1, "They should totally should guess %u\n", num);
00380 
00381       /* Play the prompt */
00382       play_files_helper(chan, cfg->global->prompt);
00383       ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
00384 
00385       for (guesses = 0; guesses < level->max_guesses; guesses++) {
00386          size_t buflen = log10(level->max_num) + 1;
00387          char buf[buflen];
00388          int guess;
00389          buf[buflen] = '\0';
00390 
00391          /* Read the number pressed */
00392          ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
00393          if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
00394             if (guesses < level->max_guesses - 1) {
00395                play_files_helper(chan, cfg->global->wrong);
00396             }
00397             continue;
00398          }
00399 
00400          /* Inform whether the guess was right, low, or high */
00401          if (guess == num && !game->cheat) {
00402             /* win */
00403             win = 1;
00404             play_files_helper(chan, cfg->global->right);
00405             guesses++;
00406             break;
00407          } else if (guess < num) {
00408             play_files_helper(chan, cfg->global->low);
00409          } else {
00410             play_files_helper(chan, cfg->global->high);
00411          }
00412 
00413          if (guesses < level->max_guesses - 1) {
00414             play_files_helper(chan, cfg->global->wrong);
00415          }
00416       }
00417 
00418       /* Process game stats */
00419       ao2_lock(level->state);
00420       if (win) {
00421          ++level->state->wins;
00422          level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
00423       } else {
00424          /* lose */
00425          level->state->losses++;
00426          play_files_helper(chan, cfg->global->lose);
00427       }
00428       ao2_unlock(level->state);
00429    }
00430 
00431    ao2_unlink(games, game);
00432 
00433    return 0;
00434 }
00435 
00436 static struct skel_level *skel_state_alloc(const char *name)
00437 {
00438    struct skel_level *level;
00439 
00440    if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
00441       return NULL;
00442    }
00443 
00444    return level;
00445 }
00446 
00447 static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
00448 {
00449    return ao2_find(tmp_container, category, OBJ_KEY);
00450 }
00451 
00452 /*! \brief Look up an existing state object, or create a new one
00453  * \internal
00454  * \note Since the reload code will create a new level from scratch, it
00455  * is important for any state that must persist between reloads to be
00456  * in a separate refcounted object. This function allows the level alloc
00457  * function to get a ref to an existing state object if it exists,
00458  * otherwise it will return a reference to a newly allocated state object.
00459  */
00460 static void *skel_find_or_create_state(const char *category)
00461 {
00462    RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00463    RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
00464    if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
00465       return skel_state_alloc(category);
00466    }
00467    ao2_ref(level->state, +1);
00468    return level->state;
00469 }
00470 
00471 static void *skel_level_alloc(const char *cat)
00472 {
00473    struct skel_level *level;
00474 
00475    if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
00476       return NULL;
00477    }
00478 
00479    if (ast_string_field_init(level, 128)) {
00480       ao2_ref(level, -1);
00481       return NULL;
00482    }
00483 
00484    /* Since the level has state information that needs to persist between reloads,
00485     * it is important to handle that here in the level's allocation function.
00486     * If not separated out into its own object, the data would be destroyed on
00487     * reload. */
00488    if (!(level->state = skel_find_or_create_state(cat))) {
00489       ao2_ref(level, -1);
00490       return NULL;
00491    }
00492 
00493    ast_string_field_set(level, name, cat);
00494 
00495    return level;
00496 }
00497 
00498 static void skel_config_destructor(void *obj)
00499 {
00500    struct skel_config *cfg = obj;
00501    ao2_cleanup(cfg->global);
00502    ao2_cleanup(cfg->levels);
00503 }
00504 
00505 static void *skel_config_alloc(void)
00506 {
00507    struct skel_config *cfg;
00508 
00509    if (!(cfg = ao2_alloc(sizeof(*cfg), skel_config_destructor))) {
00510       return NULL;
00511    }
00512 
00513    /* Allocate/initialize memory */
00514    if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
00515       goto error;
00516    }
00517 
00518    if (ast_string_field_init(cfg->global, 128)) {
00519       goto error;
00520    }
00521 
00522    if (!(cfg->levels = ao2_container_alloc(LEVEL_BUCKETS, skel_level_hash, skel_level_cmp))) {
00523       goto error;
00524    }
00525 
00526    return cfg;
00527 error:
00528    ao2_ref(cfg, -1);
00529    return NULL;
00530 }
00531 
00532 static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00533 {
00534    RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
00535 
00536    switch(cmd) {
00537    case CLI_INIT:
00538       e->command = "skel show config";
00539       e->usage =
00540          "Usage: skel show config\n"
00541          "       List app_skel global config\n";
00542       return NULL;
00543    case CLI_GENERATE:
00544       return NULL;
00545    }
00546 
00547    if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
00548       return NULL;
00549    }
00550 
00551    ast_cli(a->fd, "games per call:  %u\n", cfg->global->num_games);
00552    ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
00553    ast_cli(a->fd, "\n");
00554    ast_cli(a->fd, "Sounds\n");
00555    ast_cli(a->fd, "  prompt:      %s\n", cfg->global->prompt);
00556    ast_cli(a->fd, "  wrong guess: %s\n", cfg->global->wrong);
00557    ast_cli(a->fd, "  right guess: %s\n", cfg->global->right);
00558 
00559    return CLI_SUCCESS;
00560 }
00561 
00562 static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00563 {
00564    struct ao2_iterator iter;
00565    struct skel_current_game *game;
00566 
00567    switch(cmd) {
00568    case CLI_INIT:
00569       e->command = "skel show games";
00570       e->usage =
00571          "Usage: skel show games\n"
00572          "       List app_skel active games\n";
00573       return NULL;
00574    case CLI_GENERATE:
00575       return NULL;
00576    }
00577 
00578 #define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
00579 #define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
00580    ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
00581    iter = ao2_iterator_init(games, 0);
00582    while ((game = ao2_iterator_next(&iter))) {
00583       ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
00584       ao2_ref(game, -1);
00585    }
00586    ao2_iterator_destroy(&iter);
00587 #undef SKEL_FORMAT
00588 #undef SKEL_FORMAT1
00589    return CLI_SUCCESS;
00590 }
00591 
00592 static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00593 {
00594    RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
00595    struct ao2_iterator iter;
00596    struct skel_level *level;
00597 
00598    switch(cmd) {
00599    case CLI_INIT:
00600       e->command = "skel show levels";
00601       e->usage =
00602          "Usage: skel show levels\n"
00603          "       List the app_skel levels\n";
00604       return NULL;
00605    case CLI_GENERATE:
00606       return NULL;
00607    }
00608 
00609    if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
00610       return NULL;
00611    }
00612 
00613 #define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
00614 #define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
00615    ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
00616    iter = ao2_iterator_init(cfg->levels, 0);
00617    while ((level = ao2_iterator_next(&iter))) {
00618       ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
00619       ao2_ref(level, -1);
00620    }
00621    ao2_iterator_destroy(&iter);
00622 #undef SKEL_FORMAT
00623 #undef SKEL_FORMAT1
00624 
00625    return CLI_SUCCESS;
00626 }
00627 
00628 static struct ast_cli_entry skel_cli[] = {
00629    AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
00630    AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
00631    AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
00632 };
00633 
00634 static int reload_module(void)
00635 {
00636    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
00637       return AST_MODULE_LOAD_DECLINE;
00638    }
00639 
00640    return 0;
00641 }
00642 
00643 static int unload_module(void)
00644 {
00645    ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
00646    aco_info_destroy(&cfg_info);
00647    ao2_global_obj_release(globals);
00648    return ast_unregister_application(app);
00649 }
00650 
00651 static int load_module(void)
00652 {
00653    if (aco_info_init(&cfg_info)) {
00654       goto error;
00655    }
00656    if (!(games = ao2_container_alloc(1, NULL, NULL))) {
00657       goto error;
00658    }
00659 
00660    /* Global options */
00661    aco_option_register(&cfg_info, "games", ACO_EXACT, global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
00662    aco_option_register_custom(&cfg_info, "cheat", ACO_EXACT, global_options, "no", custom_bitfield_handler, 0);
00663 
00664    /* Sound options */
00665    aco_option_register(&cfg_info, "prompt", ACO_EXACT, sound_options, "please-enter-your&number&queue-less-than", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, prompt));
00666    aco_option_register(&cfg_info, "wrong_guess", ACO_EXACT, sound_options, "vm-pls-try-again", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, wrong));
00667    aco_option_register(&cfg_info, "right_guess", ACO_EXACT, sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
00668    aco_option_register(&cfg_info, "too_high", ACO_EXACT, sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
00669    aco_option_register(&cfg_info, "too_low", ACO_EXACT, sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
00670    aco_option_register(&cfg_info, "lose", ACO_EXACT, sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
00671 
00672    /* Level options */
00673    aco_option_register(&cfg_info, "max_number", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
00674    aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 1, FLDSET(struct skel_level, max_guesses));
00675 
00676    if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
00677       goto error;
00678    }
00679 
00680    ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
00681    if (ast_register_application_xml(app, app_exec)) {
00682       goto error;
00683    }
00684    return AST_MODULE_LOAD_SUCCESS;
00685 
00686 error:
00687    aco_info_destroy(&cfg_info);
00688    ao2_cleanup(games);
00689    return AST_MODULE_LOAD_DECLINE;
00690 }
00691 
00692 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Skeleton (sample) Application",
00693    .load = load_module,
00694    .unload = unload_module,
00695    .reload = reload_module,
00696    .load_pri = AST_MODPRI_DEFAULT,
00697 );