app_skel.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) <Year>, <Your Name Here>
  5. *
  6. * <Your Name Here> <<Your Email Here>>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. *
  18. * Please follow coding guidelines
  19. * https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
  20. */
  21. /*! \file
  22. *
  23. * \brief Skeleton application
  24. *
  25. * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
  26. *
  27. * This is a skeleton for development of an Asterisk application
  28. * \ingroup applications
  29. */
  30. /*! \li \ref app_skel.c uses configuration file \ref app_skel.conf
  31. * \addtogroup configuration_file Configuration Files
  32. */
  33. /*!
  34. * \page app_skel.conf app_skel.conf
  35. * \verbinclude app_skel.conf.sample
  36. */
  37. /*** MODULEINFO
  38. <defaultenabled>no</defaultenabled>
  39. <support_level>core</support_level>
  40. ***/
  41. #include "asterisk.h"
  42. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  43. #include <math.h> /* log10 */
  44. #include "asterisk/file.h"
  45. #include "asterisk/channel.h"
  46. #include "asterisk/pbx.h"
  47. #include "asterisk/module.h"
  48. #include "asterisk/lock.h"
  49. #include "asterisk/app.h"
  50. #include "asterisk/config.h"
  51. #include "asterisk/config_options.h"
  52. #include "asterisk/say.h"
  53. #include "asterisk/astobj2.h"
  54. #include "asterisk/acl.h"
  55. #include "asterisk/netsock2.h"
  56. #include "asterisk/strings.h"
  57. #include "asterisk/cli.h"
  58. /*** DOCUMENTATION
  59. <application name="SkelGuessNumber" language="en_US">
  60. <synopsis>
  61. An example number guessing game
  62. </synopsis>
  63. <syntax>
  64. <parameter name="level" required="true"/>
  65. <parameter name="options">
  66. <optionlist>
  67. <option name="c">
  68. <para>The computer should cheat</para>
  69. </option>
  70. <option name="n">
  71. <para>How many games to play before hanging up</para>
  72. </option>
  73. </optionlist>
  74. </parameter>
  75. </syntax>
  76. <description>
  77. <para>This simple number guessing application is a template to build other applications
  78. from. It shows you the basic structure to create your own Asterisk applications.</para>
  79. </description>
  80. </application>
  81. <configInfo name="app_skel" language="en_US">
  82. <configFile name="app_skel.conf">
  83. <configObject name="globals">
  84. <synopsis>Options that apply globally to app_skel</synopsis>
  85. <configOption name="games">
  86. <synopsis>The number of games a single execution of SkelGuessNumber will play</synopsis>
  87. </configOption>
  88. <configOption name="cheat">
  89. <synopsis>Should the computer cheat?</synopsis>
  90. <description><para>If enabled, the computer will ignore winning guesses.</para></description>
  91. </configOption>
  92. </configObject>
  93. <configObject name="sounds">
  94. <synopsis>Prompts for SkelGuessNumber to play</synopsis>
  95. <configOption name="prompt" default="please-enter-your&amp;number&amp;queue-less-than">
  96. <synopsis>A prompt directing the user to enter a number less than the max number</synopsis>
  97. </configOption>
  98. <configOption name="wrong_guess" default="vm-pls-try-again">
  99. <synopsis>The sound file to play when a wrong guess is made</synopsis>
  100. </configOption>
  101. <configOption name="right_guess" default="auth-thankyou">
  102. <synopsis>The sound file to play when a correct guess is made</synopsis>
  103. </configOption>
  104. <configOption name="too_low">
  105. <synopsis>The sound file to play when a guess is too low</synopsis>
  106. </configOption>
  107. <configOption name="too_high">
  108. <synopsis>The sound file to play when a guess is too high</synopsis>
  109. </configOption>
  110. <configOption name="lose" default="vm-goodbye">
  111. <synopsis>The sound file to play when a player loses</synopsis>
  112. </configOption>
  113. </configObject>
  114. <configObject name="level">
  115. <synopsis>Defined levels for the SkelGuessNumber game</synopsis>
  116. <configOption name="max_number">
  117. <synopsis>The maximum in the range of numbers to guess (1 is the implied minimum)</synopsis>
  118. </configOption>
  119. <configOption name="max_guesses">
  120. <synopsis>The maximum number of guesses before a game is considered lost</synopsis>
  121. </configOption>
  122. </configObject>
  123. </configFile>
  124. </configInfo>
  125. ***/
  126. static char *app = "SkelGuessNumber";
  127. enum option_flags {
  128. OPTION_CHEAT = (1 << 0),
  129. OPTION_NUMGAMES = (1 << 1),
  130. };
  131. enum option_args {
  132. OPTION_ARG_NUMGAMES,
  133. /* This *must* be the last value in this enum! */
  134. OPTION_ARG_ARRAY_SIZE,
  135. };
  136. AST_APP_OPTIONS(app_opts,{
  137. AST_APP_OPTION('c', OPTION_CHEAT),
  138. AST_APP_OPTION_ARG('n', OPTION_NUMGAMES, OPTION_ARG_NUMGAMES),
  139. });
  140. /*! \brief A structure to hold global configuration-related options */
  141. struct skel_global_config {
  142. AST_DECLARE_STRING_FIELDS(
  143. AST_STRING_FIELD(prompt); /*!< The comma-separated list of sounds to prompt to enter a number */
  144. AST_STRING_FIELD(wrong); /*!< The comma-separated list of sounds to indicate a wrong guess */
  145. AST_STRING_FIELD(right); /*!< The comma-separated list of sounds to indicate a right guess */
  146. AST_STRING_FIELD(high); /*!< The comma-separated list of sounds to indicate a high guess */
  147. AST_STRING_FIELD(low); /*!< The comma-separated list of sounds to indicate a low guess */
  148. AST_STRING_FIELD(lose); /*!< The comma-separated list of sounds to indicate a lost game */
  149. );
  150. uint32_t num_games; /*!< The number of games to play before hanging up */
  151. unsigned char cheat:1; /*!< Whether the computer can cheat or not */
  152. };
  153. /*! \brief A structure to maintain level state across reloads */
  154. struct skel_level_state {
  155. uint32_t wins; /*!< How many wins for this level */
  156. uint32_t losses; /*!< How many losses for this level */
  157. double avg_guesses; /*!< The average number of guesses to win for this level */
  158. };
  159. /*! \brief Object to hold level config information.
  160. * \note This object should hold a reference to an object that holds state across reloads.
  161. * The other fields are just examples of the kind of data that might be stored in an level.
  162. */
  163. struct skel_level {
  164. AST_DECLARE_STRING_FIELDS(
  165. AST_STRING_FIELD(name); /*!< The name of the level */
  166. );
  167. uint32_t max_num; /*!< The upper value on th range of numbers to guess */
  168. uint32_t max_guesses; /*!< The maximum number of guesses before losing */
  169. struct skel_level_state *state; /*!< A pointer to level state that must exist across all reloads */
  170. };
  171. /*! \brief Information about a currently running set of games
  172. * \note Because we want to be able to show true running information about the games
  173. * regardless of whether or not a reload has modified what the level looks like, it
  174. * is important to either copy the information we need from the level to the
  175. * current_game struct, or as we do here, store a reference to the level as it is for
  176. * the running game.
  177. */
  178. struct skel_current_game {
  179. uint32_t total_games; /*! The total number of games for this call to the app */
  180. uint32_t games_left; /*! How many games are left to play in this set */
  181. uint32_t cheat; /*! Whether or not cheating was enabled for the game */
  182. struct skel_level *level_info; /*! The level information for the running game */
  183. };
  184. /* Treat the levels as an array--there won't be many and this will maintain the order */
  185. #define LEVEL_BUCKETS 1
  186. /*! \brief A container that holds all config-related information
  187. * \note This object should contain a pointer to structs for global data and containers for
  188. * any levels that are configured. Objects of this type will be swapped out on reload. If an
  189. * level needs to maintain state across reloads, it needs to allocate a refcounted object to
  190. * hold that state and ensure that a reference is passed to that state when creating a new
  191. * level for reload. */
  192. struct skel_config {
  193. struct skel_global_config *global;
  194. struct ao2_container *levels;
  195. };
  196. /* Config Options API callbacks */
  197. /*! \brief Allocate a skel_config to hold a snapshot of the complete results of parsing a config
  198. * \internal
  199. * \returns A void pointer to a newly allocated skel_config
  200. */
  201. static void *skel_config_alloc(void);
  202. /*! \brief Allocate a skel_level based on a category in a configuration file
  203. * \param cat The category to base the level on
  204. * \returns A void pointer to a newly allocated skel_level
  205. */
  206. static void *skel_level_alloc(const char *cat);
  207. /*! \brief Find a skel level in the specified container
  208. * \note This function *does not* look for a skel_level in the active container. It is used
  209. * internally by the Config Options code to check if an level has already been added to the
  210. * container that will be swapped for the live container on a successul reload.
  211. *
  212. * \param tmp_container A non-active container to search for a level
  213. * \param category The category associated with the level to check for
  214. * \retval non-NULL The level from the container
  215. * \retval NULL The level does not exist in the container
  216. */
  217. static void *skel_level_find(struct ao2_container *tmp_container, const char *category);
  218. /*! \brief An aco_type structure to link the "general" category to the skel_global_config type */
  219. static struct aco_type global_option = {
  220. .type = ACO_GLOBAL,
  221. .name = "globals",
  222. .item_offset = offsetof(struct skel_config, global),
  223. .category_match = ACO_WHITELIST_EXACT,
  224. .category = "general",
  225. };
  226. struct aco_type *global_options[] = ACO_TYPES(&global_option);
  227. /*! \brief An aco_type structure to link the "sounds" category to the skel_global_config type */
  228. static struct aco_type sound_option = {
  229. .type = ACO_GLOBAL,
  230. .name = "sounds",
  231. .item_offset = offsetof(struct skel_config, global),
  232. .category_match = ACO_WHITELIST_EXACT,
  233. .category = "sounds",
  234. };
  235. struct aco_type *sound_options[] = ACO_TYPES(&sound_option);
  236. static const char *level_categories[] = {
  237. "general",
  238. "sounds",
  239. NULL,
  240. };
  241. /*! \brief An aco_type structure to link the everything but the "general" and "sounds" categories to the skel_level type */
  242. static struct aco_type level_option = {
  243. .type = ACO_ITEM,
  244. .name = "level",
  245. .category_match = ACO_BLACKLIST_ARRAY,
  246. .category = (const char *)level_categories,
  247. .item_alloc = skel_level_alloc,
  248. .item_find = skel_level_find,
  249. .item_offset = offsetof(struct skel_config, levels),
  250. };
  251. struct aco_type *level_options[] = ACO_TYPES(&level_option);
  252. struct aco_file app_skel_conf = {
  253. .filename = "app_skel.conf",
  254. .types = ACO_TYPES(&global_option, &sound_option, &level_option),
  255. };
  256. /*! \brief A global object container that will contain the skel_config that gets swapped out on reloads */
  257. static AO2_GLOBAL_OBJ_STATIC(globals);
  258. /*! \brief The container of active games */
  259. static struct ao2_container *games;
  260. /*! \brief Register information about the configs being processed by this module */
  261. CONFIG_INFO_STANDARD(cfg_info, globals, skel_config_alloc,
  262. .files = ACO_FILES(&app_skel_conf),
  263. );
  264. static void skel_global_config_destructor(void *obj)
  265. {
  266. struct skel_global_config *global = obj;
  267. ast_string_field_free_memory(global);
  268. }
  269. static void skel_game_destructor(void *obj)
  270. {
  271. struct skel_current_game *game = obj;
  272. ao2_cleanup(game->level_info);
  273. }
  274. static void skel_state_destructor(void *obj)
  275. {
  276. return;
  277. }
  278. static struct skel_current_game *skel_game_alloc(struct skel_level *level)
  279. {
  280. struct skel_current_game *game;
  281. if (!(game = ao2_alloc(sizeof(struct skel_current_game), skel_game_destructor))) {
  282. return NULL;
  283. }
  284. ao2_ref(level, +1);
  285. game->level_info = level;
  286. return game;
  287. }
  288. static void skel_level_destructor(void *obj)
  289. {
  290. struct skel_level *level = obj;
  291. ast_string_field_free_memory(level);
  292. ao2_cleanup(level->state);
  293. }
  294. static int skel_level_hash(const void *obj, const int flags)
  295. {
  296. const struct skel_level *level = obj;
  297. const char *name = (flags & OBJ_KEY) ? obj : level->name;
  298. return ast_str_case_hash(name);
  299. }
  300. static int skel_level_cmp(void *obj, void *arg, int flags)
  301. {
  302. struct skel_level *one = obj, *two = arg;
  303. const char *match = (flags & OBJ_KEY) ? arg : two->name;
  304. return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
  305. }
  306. /*! \brief A custom bitfield handler
  307. * \internal
  308. * \note It is not possible to take the address of a bitfield, therefor all
  309. * bitfields in the config struct will have to use a custom handler
  310. * \param opt The opaque config option
  311. * \param var The ast_variable containing the option name and value
  312. * \param obj The object registerd for this option type
  313. * \retval 0 Success
  314. * \retval non-zero Failure
  315. */
  316. static int custom_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
  317. {
  318. struct skel_global_config *global = obj;
  319. if (!strcasecmp(var->name, "cheat")) {
  320. global->cheat = ast_true(var->value);
  321. } else {
  322. return -1;
  323. }
  324. return 0;
  325. }
  326. static void play_files_helper(struct ast_channel *chan, const char *prompts)
  327. {
  328. char *prompt, *rest = ast_strdupa(prompts);
  329. ast_stopstream(chan);
  330. while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
  331. ast_stopstream(chan);
  332. }
  333. }
  334. static int app_exec(struct ast_channel *chan, const char *data)
  335. {
  336. int win = 0;
  337. uint32_t guesses;
  338. RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  339. RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
  340. RAII_VAR(struct skel_current_game *, game, NULL, ao2_cleanup);
  341. char *parse, *opts[OPTION_ARG_ARRAY_SIZE];
  342. struct ast_flags flags;
  343. AST_DECLARE_APP_ARGS(args,
  344. AST_APP_ARG(level);
  345. AST_APP_ARG(options);
  346. );
  347. if (!cfg) {
  348. ast_log(LOG_ERROR, "Couldn't access configuratino data!\n");
  349. return -1;
  350. }
  351. if (ast_strlen_zero(data)) {
  352. ast_log(LOG_WARNING, "%s requires an argument (level[,options])\n", app);
  353. return -1;
  354. }
  355. /* We need to make a copy of the input string if we are going to modify it! */
  356. parse = ast_strdupa(data);
  357. AST_STANDARD_APP_ARGS(args, parse);
  358. if (args.argc == 2) {
  359. ast_app_parse_options(app_opts, &flags, opts, args.options);
  360. }
  361. if (ast_strlen_zero(args.level)) {
  362. ast_log(LOG_ERROR, "%s requires a level argument\n", app);
  363. return -1;
  364. }
  365. if (!(level = ao2_find(cfg->levels, args.level, OBJ_KEY))) {
  366. ast_log(LOG_ERROR, "Unknown level: %s\n", args.level);
  367. return -1;
  368. }
  369. if (!(game = skel_game_alloc(level))) {
  370. return -1;
  371. }
  372. ao2_link(games, game);
  373. /* Use app-specified values, or the options specified in [general] if they aren't passed to the app */
  374. if (!ast_test_flag(&flags, OPTION_NUMGAMES) ||
  375. ast_strlen_zero(opts[OPTION_ARG_NUMGAMES]) ||
  376. ast_parse_arg(opts[OPTION_ARG_NUMGAMES], PARSE_UINT32, &game->total_games)) {
  377. game->total_games = cfg->global->num_games;
  378. }
  379. game->games_left = game->total_games;
  380. game->cheat = ast_test_flag(&flags, OPTION_CHEAT) || cfg->global->cheat;
  381. for (game->games_left = game->total_games; game->games_left; game->games_left--) {
  382. uint32_t num = ast_random() % level->max_num; /* random number between 0 and level->max_num */
  383. ast_debug(1, "They should totally should guess %u\n", num);
  384. /* Play the prompt */
  385. play_files_helper(chan, cfg->global->prompt);
  386. ast_say_number(chan, level->max_num, "", ast_channel_language(chan), "");
  387. for (guesses = 0; guesses < level->max_guesses; guesses++) {
  388. size_t buflen = log10(level->max_num) + 1;
  389. char buf[buflen];
  390. int guess;
  391. buf[buflen] = '\0';
  392. /* Read the number pressed */
  393. ast_readstring(chan, buf, buflen - 1, 2000, 10000, "");
  394. if (ast_parse_arg(buf, PARSE_INT32 | PARSE_IN_RANGE, &guess, 0, level->max_num)) {
  395. if (guesses < level->max_guesses - 1) {
  396. play_files_helper(chan, cfg->global->wrong);
  397. }
  398. continue;
  399. }
  400. /* Inform whether the guess was right, low, or high */
  401. if (guess == num && !game->cheat) {
  402. /* win */
  403. win = 1;
  404. play_files_helper(chan, cfg->global->right);
  405. guesses++;
  406. break;
  407. } else if (guess < num) {
  408. play_files_helper(chan, cfg->global->low);
  409. } else {
  410. play_files_helper(chan, cfg->global->high);
  411. }
  412. if (guesses < level->max_guesses - 1) {
  413. play_files_helper(chan, cfg->global->wrong);
  414. }
  415. }
  416. /* Process game stats */
  417. ao2_lock(level->state);
  418. if (win) {
  419. ++level->state->wins;
  420. level->state->avg_guesses = ((level->state->wins - 1) * level->state->avg_guesses + guesses) / level->state->wins;
  421. } else {
  422. /* lose */
  423. level->state->losses++;
  424. play_files_helper(chan, cfg->global->lose);
  425. }
  426. ao2_unlock(level->state);
  427. }
  428. ao2_unlink(games, game);
  429. return 0;
  430. }
  431. static struct skel_level *skel_state_alloc(const char *name)
  432. {
  433. struct skel_level *level;
  434. if (!(level = ao2_alloc(sizeof(*level), skel_state_destructor))) {
  435. return NULL;
  436. }
  437. return level;
  438. }
  439. static void *skel_level_find(struct ao2_container *tmp_container, const char *category)
  440. {
  441. return ao2_find(tmp_container, category, OBJ_KEY);
  442. }
  443. /*! \brief Look up an existing state object, or create a new one
  444. * \internal
  445. * \note Since the reload code will create a new level from scratch, it
  446. * is important for any state that must persist between reloads to be
  447. * in a separate refcounted object. This function allows the level alloc
  448. * function to get a ref to an existing state object if it exists,
  449. * otherwise it will return a reference to a newly allocated state object.
  450. */
  451. static void *skel_find_or_create_state(const char *category)
  452. {
  453. RAII_VAR(struct skel_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
  454. RAII_VAR(struct skel_level *, level, NULL, ao2_cleanup);
  455. if (!cfg || !cfg->levels || !(level = ao2_find(cfg->levels, category, OBJ_KEY))) {
  456. return skel_state_alloc(category);
  457. }
  458. ao2_ref(level->state, +1);
  459. return level->state;
  460. }
  461. static void *skel_level_alloc(const char *cat)
  462. {
  463. struct skel_level *level;
  464. if (!(level = ao2_alloc(sizeof(*level), skel_level_destructor))) {
  465. return NULL;
  466. }
  467. if (ast_string_field_init(level, 128)) {
  468. ao2_ref(level, -1);
  469. return NULL;
  470. }
  471. /* Since the level has state information that needs to persist between reloads,
  472. * it is important to handle that here in the level's allocation function.
  473. * If not separated out into its own object, the data would be destroyed on
  474. * reload. */
  475. if (!(level->state = skel_find_or_create_state(cat))) {
  476. ao2_ref(level, -1);
  477. return NULL;
  478. }
  479. ast_string_field_set(level, name, cat);
  480. return level;
  481. }
  482. static void skel_config_destructor(void *obj)
  483. {
  484. struct skel_config *cfg = obj;
  485. ao2_cleanup(cfg->global);
  486. ao2_cleanup(cfg->levels);
  487. }
  488. static void *skel_config_alloc(void)
  489. {
  490. struct skel_config *cfg;
  491. if (!(cfg = ao2_alloc(sizeof(*cfg), skel_config_destructor))) {
  492. return NULL;
  493. }
  494. /* Allocate/initialize memory */
  495. if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), skel_global_config_destructor))) {
  496. goto error;
  497. }
  498. if (ast_string_field_init(cfg->global, 128)) {
  499. goto error;
  500. }
  501. cfg->levels = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, LEVEL_BUCKETS,
  502. skel_level_hash, NULL, skel_level_cmp);
  503. if (!cfg->levels) {
  504. goto error;
  505. }
  506. return cfg;
  507. error:
  508. ao2_ref(cfg, -1);
  509. return NULL;
  510. }
  511. static char *handle_skel_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  512. {
  513. RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
  514. switch(cmd) {
  515. case CLI_INIT:
  516. e->command = "skel show config";
  517. e->usage =
  518. "Usage: skel show config\n"
  519. " List app_skel global config\n";
  520. return NULL;
  521. case CLI_GENERATE:
  522. return NULL;
  523. }
  524. if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->global) {
  525. return NULL;
  526. }
  527. ast_cli(a->fd, "games per call: %u\n", cfg->global->num_games);
  528. ast_cli(a->fd, "computer cheats: %s\n", AST_CLI_YESNO(cfg->global->cheat));
  529. ast_cli(a->fd, "\n");
  530. ast_cli(a->fd, "Sounds\n");
  531. ast_cli(a->fd, " prompt: %s\n", cfg->global->prompt);
  532. ast_cli(a->fd, " wrong guess: %s\n", cfg->global->wrong);
  533. ast_cli(a->fd, " right guess: %s\n", cfg->global->right);
  534. return CLI_SUCCESS;
  535. }
  536. static char *handle_skel_show_games(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  537. {
  538. struct ao2_iterator iter;
  539. struct skel_current_game *game;
  540. switch(cmd) {
  541. case CLI_INIT:
  542. e->command = "skel show games";
  543. e->usage =
  544. "Usage: skel show games\n"
  545. " List app_skel active games\n";
  546. return NULL;
  547. case CLI_GENERATE:
  548. return NULL;
  549. }
  550. #define SKEL_FORMAT "%-15.15s %-15.15s %-15.15s\n"
  551. #define SKEL_FORMAT1 "%-15.15s %-15u %-15u\n"
  552. ast_cli(a->fd, SKEL_FORMAT, "Level", "Total Games", "Games Left");
  553. iter = ao2_iterator_init(games, 0);
  554. while ((game = ao2_iterator_next(&iter))) {
  555. ast_cli(a->fd, SKEL_FORMAT1, game->level_info->name, game->total_games, game->games_left);
  556. ao2_ref(game, -1);
  557. }
  558. ao2_iterator_destroy(&iter);
  559. #undef SKEL_FORMAT
  560. #undef SKEL_FORMAT1
  561. return CLI_SUCCESS;
  562. }
  563. static char *handle_skel_show_levels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  564. {
  565. RAII_VAR(struct skel_config *, cfg, NULL, ao2_cleanup);
  566. struct ao2_iterator iter;
  567. struct skel_level *level;
  568. switch(cmd) {
  569. case CLI_INIT:
  570. e->command = "skel show levels";
  571. e->usage =
  572. "Usage: skel show levels\n"
  573. " List the app_skel levels\n";
  574. return NULL;
  575. case CLI_GENERATE:
  576. return NULL;
  577. }
  578. if (!(cfg = ao2_global_obj_ref(globals)) || !cfg->levels) {
  579. return NULL;
  580. }
  581. #define SKEL_FORMAT "%-15.15s %-11.11s %-12.12s %-8.8s %-8.8s %-12.12s\n"
  582. #define SKEL_FORMAT1 "%-15.15s %-11u %-12u %-8u %-8u %-8f\n"
  583. ast_cli(a->fd, SKEL_FORMAT, "Name", "Max number", "Max Guesses", "Wins", "Losses", "Avg Guesses");
  584. iter = ao2_iterator_init(cfg->levels, 0);
  585. while ((level = ao2_iterator_next(&iter))) {
  586. ast_cli(a->fd, SKEL_FORMAT1, level->name, level->max_num, level->max_guesses, level->state->wins, level->state->losses, level->state->avg_guesses);
  587. ao2_ref(level, -1);
  588. }
  589. ao2_iterator_destroy(&iter);
  590. #undef SKEL_FORMAT
  591. #undef SKEL_FORMAT1
  592. return CLI_SUCCESS;
  593. }
  594. static struct ast_cli_entry skel_cli[] = {
  595. AST_CLI_DEFINE(handle_skel_show_config, "Show app_skel global config options"),
  596. AST_CLI_DEFINE(handle_skel_show_levels, "Show app_skel levels"),
  597. AST_CLI_DEFINE(handle_skel_show_games, "Show app_skel active games"),
  598. };
  599. static int reload_module(void)
  600. {
  601. if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
  602. return AST_MODULE_LOAD_DECLINE;
  603. }
  604. return 0;
  605. }
  606. static int unload_module(void)
  607. {
  608. ast_cli_unregister_multiple(skel_cli, ARRAY_LEN(skel_cli));
  609. aco_info_destroy(&cfg_info);
  610. ao2_global_obj_release(globals);
  611. ao2_cleanup(games);
  612. return ast_unregister_application(app);
  613. }
  614. /*!
  615. * \brief Load the module
  616. *
  617. * Module loading including tests for configuration or dependencies.
  618. * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  619. * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
  620. * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
  621. * configuration file or other non-critical problem return
  622. * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  623. */
  624. static int load_module(void)
  625. {
  626. if (aco_info_init(&cfg_info)) {
  627. goto error;
  628. }
  629. games = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
  630. if (!games) {
  631. goto error;
  632. }
  633. /* Global options */
  634. aco_option_register(&cfg_info, "games", ACO_EXACT, global_options, "3", OPT_UINT_T, 0, FLDSET(struct skel_global_config, num_games));
  635. aco_option_register_custom(&cfg_info, "cheat", ACO_EXACT, global_options, "no", custom_bitfield_handler, 0);
  636. /* Sound options */
  637. 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));
  638. 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));
  639. aco_option_register(&cfg_info, "right_guess", ACO_EXACT, sound_options, "auth-thankyou", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, right));
  640. aco_option_register(&cfg_info, "too_high", ACO_EXACT, sound_options, "high", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, high));
  641. aco_option_register(&cfg_info, "too_low", ACO_EXACT, sound_options, "low", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, low));
  642. aco_option_register(&cfg_info, "lose", ACO_EXACT, sound_options, "vm-goodbye", OPT_STRINGFIELD_T, 0, STRFLDSET(struct skel_global_config, lose));
  643. /* Level options */
  644. aco_option_register(&cfg_info, "max_number", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_num));
  645. aco_option_register(&cfg_info, "max_guesses", ACO_EXACT, level_options, NULL, OPT_UINT_T, 0, FLDSET(struct skel_level, max_guesses));
  646. if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
  647. goto error;
  648. }
  649. ast_cli_register_multiple(skel_cli, ARRAY_LEN(skel_cli));
  650. if (ast_register_application_xml(app, app_exec)) {
  651. goto error;
  652. }
  653. return AST_MODULE_LOAD_SUCCESS;
  654. error:
  655. aco_info_destroy(&cfg_info);
  656. ao2_cleanup(games);
  657. return AST_MODULE_LOAD_DECLINE;
  658. }
  659. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skeleton (sample) Application",
  660. .support_level = AST_MODULE_SUPPORT_CORE,
  661. .load = load_module,
  662. .unload = unload_module,
  663. .reload = reload_module,
  664. );