Asterisk - The Open Source Telephony Project  18.5.0
test_locale.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Digium, Inc.
5  *
6  * Tilghman Lesher <tlesher AT digium DOT com>
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 
19 /*! \file
20  *
21  * \brief Locale Test
22  *
23  * \author\verbatim Tilghman Lesher <tlesher AT digium DOT com> \endverbatim
24  *
25  * \ingroup tests
26  */
27 
28 /*** MODULEINFO
29  <depend>TEST_FRAMEWORK</depend>
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include <sys/types.h>
36 #include <dirent.h>
37 #ifndef __USE_GNU
38 #define __USE_GNU 1
39 #endif
40 #include <locale.h>
41 
42 #include "asterisk/cli.h"
43 #include "asterisk/linkedlists.h"
44 #include "asterisk/localtime.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/module.h"
47 
48 
49 static char *handle_cli_test_locales(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
50 {
51  DIR *localedir;
52  struct dirent *dent;
53  struct ast_tm atm;
54  struct timeval tv;
55  const char *orig_locale;
56  char origlocalformat[200] = "", localformat[200] = "";
57  struct test_locales {
58  AST_LIST_ENTRY(test_locales) list;
59  char *localformat;
60  char name[0];
61  } *tl = NULL;
62  AST_LIST_HEAD_NOLOCK(locales, test_locales) locales;
63  int varies = 0, all_successful = 1, count = 0, count_fail = 0;
64 
65  switch (cmd) {
66  case CLI_INIT:
67  e->command = "test locale";
68  e->usage = ""
69  "Usage: test locale\n"
70  " Test thread safety of locale functions.\n";
71  return NULL;
72  case CLI_GENERATE:
73  return NULL;
74  }
75 
76  if (a->argc != e->args) {
77  return CLI_SHOWUSAGE;
78  }
79 
80  /* First we run a set of tests with the global locale, which isn't thread-safe. */
81  if (!(localedir = opendir(
82 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
83  "/usr/share/locale"
84 #else /* Linux */
85  "/usr/lib/locale"
86 #endif
87  ))) {
88  ast_cli(a->fd, "No locales seem to exist on this platform.\n");
89  return CLI_SUCCESS;
90  }
91 
92  tv = ast_tvnow();
93  ast_localtime(&tv, &atm, NULL);
94  orig_locale = setlocale(LC_ALL, NULL);
95  AST_LIST_HEAD_SET_NOLOCK(&locales, NULL);
96 
97  /* Get something different, to compare against. */
98  ast_strftime(origlocalformat, sizeof(origlocalformat), "%c", &atm);
99 
100  while ((dent = readdir(localedir))) {
101  size_t locallen;
102  size_t namelen;
103 
104  if (dent->d_name[0] == '.') {
105  continue;
106  }
107 
108  setlocale(LC_ALL, dent->d_name);
109  ast_strftime(localformat, sizeof(localformat), "%c", &atm);
110 
111  locallen = strlen(localformat) + 1;
112  namelen = strlen(dent->d_name) + 1;
113 
114  /* Store values */
115  if (!(tl = ast_calloc(1, sizeof(*tl) + locallen + namelen))) {
116  continue;
117  }
118 
119  ast_copy_string(tl->name, dent->d_name, namelen); /* SAFE */
120  tl->localformat = tl->name + namelen;
121  ast_copy_string(tl->localformat, localformat, locallen); /* SAFE */
122 
123  AST_LIST_INSERT_TAIL(&locales, tl, list);
124 
125  /* Ensure that at least two entries differ, otherwise this test doesn't mean much. */
126  if (!varies && strcmp(AST_LIST_FIRST(&locales)->localformat, localformat)) {
127  varies = 1;
128  }
129  }
130 
131  setlocale(LC_ALL, orig_locale);
132 
133  closedir(localedir);
134 
135  if (!varies) {
136  if (!strcmp(origlocalformat, localformat)) {
137  ast_cli(a->fd, "WARNING: the locales on your system don't differ. Install more locales if you want this test to mean something.\n");
138  }
139  }
140 
141  orig_locale = ast_setlocale(AST_LIST_FIRST(&locales)->name);
142 
143  while ((tl = AST_LIST_REMOVE_HEAD(&locales, list))) {
144  ast_setlocale(tl->name);
145  ast_strftime(localformat, sizeof(localformat), "%c", &atm);
146  if (strcmp(localformat, tl->localformat)) {
147  ast_cli(a->fd, "WARNING: locale test fails for locale %s\n", tl->name);
148  all_successful = 0;
149  count_fail++;
150  }
151  ast_free(tl);
152  count++;
153  }
154 
155  ast_setlocale(orig_locale);
156 
157  if (all_successful) {
158  ast_cli(a->fd, "All %d locale tests successful\n", count);
159  } else if (count_fail == count && count > 0) {
160  ast_cli(a->fd, "No locale tests successful out of %d tries\n", count);
161  } else if (count > 0) {
162  ast_cli(a->fd, "Partial failure (%d/%d) for a %.0f%% failure rate\n", count_fail, count, count_fail * 100.0 / count);
163  } else {
164  ast_cli(a->fd, "No locales tested. Install more locales.\n");
165  }
166 
167  return CLI_SUCCESS;
168 }
169 
170 static struct ast_cli_entry cli_locales[] = {
171  AST_CLI_DEFINE(handle_cli_test_locales, "Test locales for thread-safety"),
172 };
173 
174 static int unload_module(void)
175 {
176  ast_cli_unregister_multiple(cli_locales, ARRAY_LEN(cli_locales));
177  return 0;
178 }
179 
180 static int load_module(void)
181 {
182  ast_cli_register_multiple(cli_locales, ARRAY_LEN(cli_locales));
184 }
185 
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
#define __APPLE__
Definition: mpool.c:316
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
Definition: cli.h:152
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
Utility functions.
int args
This gets set in ast_cli_register()
Definition: cli.h:185
Custom localtime functions for multiple timezones.
static char * handle_cli_test_locales(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: test_locale.c:49
const int fd
Definition: cli.h:159
static int unload_module(void)
Definition: test_locale.c:174
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define AST_LIST_HEAD_SET_NOLOCK(head, entry)
Initializes a list head structure with a specified first entry.
Definition: linkedlists.h:387
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
const char * ast_setlocale(const char *locale)
Set the thread-local representation of the current locale.
Definition: localtime.c:2420
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static int load_module(void)
Definition: test_locale.c:180
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static struct ast_cli_entry cli_locales[]
Definition: test_locale.c:170
static struct test_val a