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

Generated on Thu Apr 16 06:27:12 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6