GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / saslcache.c
1 /*******************************************************************************
2  * *****************************************************************************
3  * *
4  * * saslcache.c
5  * *
6  * * Description:  A small utility that can attach to saslauthd's shared
7  * *               memory region and display/dump information in the cache.
8  * *
9  * * Copyright (C) 2003 Jeremy Rumpf
10  * *
11  * * Redistribution and use in source and binary forms, with or without
12  * * modification, are permitted provided that the following conditions
13  * * are met:
14  * *
15  * * 1. Redistributions of source code must retain the above copyright
16  * *    notice, this list of conditions and the following disclaimer.
17  * *
18  * * 2. Redistributions in binary form must reproduce the above copyright
19  * *    notice, this list of conditions and the following disclaimer in the
20  * *    documentation and/or other materials provided with the distribution.
21  * *
22  * * THIS SOFTWARE IS PROVIDED ``AS IS''. ANY EXPRESS OR IMPLIED WARRANTIES,
23  * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * * IN NO EVENT SHALL JEREMY RUMPF OR ANY CONTRIBUTER TO THIS SOFTWARE BE
25  * * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
27  * * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * * THE POSSIBILITY OF SUCH DAMAGE
32  * *
33  * * Jeremy Rumpf
34  * * jrumpf@heavyload.net
35  * *
36  * ******************************************************************************
37  ********************************************************************************/
38
39 /****************************************
40 * * includes
41 *****************************************/
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/fcntl.h>
45 #include <sys/mman.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <time.h>
51
52 #include "cache.h"
53
54
55 /****************************************
56 * * declarations/protos
57 *****************************************/
58 void    show_usage(void);
59 void    dump_cache_stats(void);
60 void    dump_cache_users(void);
61 char    *make_time(time_t);
62
63 /****************************************
64 * * module globals
65 *****************************************/
66 static  void            *shm_base = NULL;
67 static  struct bucket   *table = NULL;
68 static  struct stats    *table_stats = NULL;
69
70 /****************************************
71 *****************************************/
72
73
74 /*************************************************************
75 * * Main
76 **************************************************************/
77 int main(int argc, char **argv) {
78
79         int             option;
80         int             dump_user_info = 0;
81         int             dump_stat_info = 0;
82         char            *file = NULL;
83         int             file_fd;
84         int             shmid = 0;
85         char            shmid_buff[256];
86         char            cache_magic[64];
87         struct stat     stat_buff;
88
89         while ((option = getopt(argc, argv, "dm:s")) != -1) {
90                 switch(option) {
91
92                         case 'd':
93                                 dump_user_info = 1;
94                                 break;
95
96                         case 's':
97                                 dump_stat_info = 1;
98                                 break;
99
100                         case 'm':
101                                 file = strdup(optarg);
102                                 break;
103
104                         default:
105                                 show_usage();
106                 }
107         }
108
109         if (file == NULL)
110                 file = PATH_SASLAUTHD_RUNDIR "/cache.mmap";
111
112         if (stat(file, &stat_buff) == -1) {
113                 fprintf(stderr, "could not stat mmap file: %s\n", file);
114                 fprintf(stderr, "stat: %s\n", strerror(errno));
115                 exit(1);
116         }
117
118         if ((file_fd = open(file, O_RDONLY)) < 0) {
119                 fprintf(stderr, "could not open mmap file: %s\n", file);
120                 fprintf(stderr, "open: %s\n", strerror(errno));
121                 fprintf(stderr, "perhaps saslcache -m <path>\n");
122                 exit(1);
123         }
124
125         if ((shm_base = mmap(NULL, stat_buff.st_size, PROT_READ, MAP_SHARED, file_fd, 0))== (void *)-1) {
126                 fprintf(stderr, "could not mmap shared memory file: %s\n", file);
127                 fprintf(stderr, "mmap: %s\n", strerror(errno));
128                 exit(1);
129         }       
130
131         memcpy(cache_magic, shm_base, 64);
132         cache_magic[63] = '\0';
133
134         if (strcmp(cache_magic, CACHE_CACHE_MAGIC) != 0) {
135                 fprintf(stderr, "mmap file [%s] is not a valid saslauthd cache\n", file);
136                 exit(1);
137         }
138
139         table_stats = shm_base + 64;
140         (char *)table = (char *)table_stats + 128;
141
142         if (dump_stat_info == 0 && dump_user_info == 0)
143                 dump_stat_info = 1;
144
145         if (dump_stat_info)
146                 dump_cache_stats();
147
148         if (dump_user_info)
149                 dump_cache_users();
150
151         exit(0);        
152 }
153
154
155 /****************************************************
156 * * Dump a delimited record for each item in the
157 * * cache to stdout.
158 ****************************************************/
159 void dump_cache_users(void) {
160
161         unsigned int            x;
162         struct bucket           *ref_bucket;
163         time_t                  epoch_to;
164
165         epoch_to = time(NULL) - table_stats->timeout;
166
167         fprintf(stdout, "\"user\",\"realm\",\"service\",\"created\",\"created_localtime\"\n");
168
169         for (x = 0; x < (table_stats->table_size * table_stats->max_buckets_per); x++) {
170
171                 ref_bucket = table + x;
172
173                 if (ref_bucket->created > epoch_to && *(ref_bucket->creds) != '\0') {
174                         fprintf(stderr, "\"%s\",", ref_bucket->creds + ref_bucket->user_offt);
175                         fprintf(stderr, "\"%s\",", ref_bucket->creds + ref_bucket->realm_offt);
176                         fprintf(stderr, "\"%s\",", ref_bucket->creds + ref_bucket->service_offt);
177                         fprintf(stderr, "\"%lu\",", ref_bucket->created);
178                         fprintf(stderr, "\"%s\"\n", make_time(ref_bucket->created));
179                 }
180         }
181 }
182
183 /****************************************************
184 * * Dump some usage statistics about the cred cache.
185 * * (clean this up someday)
186 ****************************************************/
187 void dump_cache_stats(void) {
188
189         unsigned int            x, y, z;
190         float                   a;
191         unsigned int            max_chain_length = 0;
192         unsigned int            min_chain_length = 0;
193         unsigned int            buckets_in_use = 0;
194         unsigned int            slots_in_use = 0;
195         unsigned int            slots_max_chain = 0;
196         unsigned int            slots_min_chain = 0;
197         time_t                  epoch_to;
198
199
200         min_chain_length = table_stats->max_buckets_per;
201         epoch_to = time(NULL) - table_stats->timeout;
202
203         for (x = 0; x < table_stats->table_size; x++) {
204
205                 z = 0;
206
207                 for (y = (x * table_stats->max_buckets_per); y < ((x + 1) * table_stats->max_buckets_per); y++) { 
208                         if (table[y].created > epoch_to) {
209                                 buckets_in_use++;
210                                 z++;
211                         }
212                 }
213
214                 if (z == min_chain_length)
215                         slots_min_chain++;
216
217                 if (z == max_chain_length)
218                         slots_max_chain++;
219
220                 if (z > 0)
221                         slots_in_use++;
222
223                 if (z > max_chain_length) {
224                         max_chain_length = z;
225                         slots_max_chain = 1;
226                 }
227
228                 if (z < min_chain_length) {
229                         min_chain_length = z;
230                         slots_min_chain = 1;
231                 }
232         }
233
234         fprintf(stdout, "----------------------------------------\n");
235         fprintf(stdout, "Saslauthd Cache Detail:\n");
236         fprintf(stdout, "\n");
237         fprintf(stdout, "  timeout (seconds)           :  %d\n", table_stats->timeout);
238         fprintf(stdout, "  total slots allocated       :  %d\n", table_stats->table_size);
239         fprintf(stdout, "  slots in use                :  %d\n", slots_in_use);
240         fprintf(stdout, "  total buckets               :  %d\n", (table_stats->max_buckets_per * table_stats->table_size));
241         fprintf(stdout, "  buckets per slot            :  %d\n", table_stats->max_buckets_per);
242         fprintf(stdout, "  buckets in use              :  %d\n", buckets_in_use);
243         fprintf(stdout, "  hash table size (bytes)     :  %d\n", table_stats->bytes);
244         fprintf(stdout, "  bucket size (bytes)         :  %d\n", table_stats->sizeof_bucket);
245         fprintf(stdout, "  minimum slot allocation     :  %d\n", min_chain_length);
246         fprintf(stdout, "  maximum slot allocation     :  %d\n", max_chain_length);
247         fprintf(stdout, "  slots at maximum allocation :  %d\n", slots_max_chain);
248         fprintf(stdout, "  slots at minimum allocation :  %d\n", slots_min_chain);
249
250         if (table_stats->table_size == 0)
251                 a = 0;
252         else
253                 a = slots_in_use / (float)table_stats->table_size;
254
255         fprintf(stdout, "  overall hash table load     :  %0.2f\n", a);
256         fprintf(stdout, "\n");
257         fprintf(stdout, "  hits*                       :  %d\n", table_stats->hits);
258         fprintf(stdout, "  misses*                     :  %d\n", table_stats->misses);
259         fprintf(stdout, "  total lookup attempts*      :  %d\n", table_stats->attempts);
260
261         if (table_stats->attempts == 0)
262                 a = 0;
263         else
264                 a = (table_stats->hits / (float)table_stats->attempts) * 100;
265
266         fprintf(stdout, "  hit ratio*                  :  %0.2f\n", a);
267         fprintf(stdout, "  flock failures*             :  %d\n", table_stats->lock_failures);
268         fprintf(stdout, "----------------------------------------\n");
269         fprintf(stdout, "* May not be completely accurate\n");
270         fprintf(stdout, "----------------------------------------\n\n");
271 }
272
273
274 /**************************************************
275 * * Create a human readable time representation
276 ****************************************************/
277 char    *make_time(time_t epoch) {
278
279         static char     created_str[128];
280         struct  tm      *tm_st = NULL;
281
282
283         tm_st = localtime(&epoch);      
284
285         if (tm_st == NULL) 
286                 return "unknown";
287
288         strftime(created_str, 127, "%c", tm_st);
289         created_str[127] = '\0';
290
291         return created_str;
292 }
293
294
295 /**************************************************
296 * * Dump out the usage information and exit
297 ****************************************************/
298 void show_usage(void) {
299
300     fprintf(stderr, "usage: saslcache [options]\n\n");
301     fprintf(stderr, "option information:\n");
302     fprintf(stderr, "  -d             Dumps a csv list of information in the cache.\n");
303     fprintf(stderr, "  -f             Purges all entries from the cache.\n");
304     fprintf(stderr, "  -m <path>      Alternate path to the cache.mmap file.\n");
305     fprintf(stderr, "                 Defaults to: %s\n", PATH_SASLAUTHD_RUNDIR "/cache.mmap");
306     fprintf(stderr, "  -s             Dumps general statistic information about the cache.\n");
307     fprintf(stderr, "\n");
308     fprintf(stderr, "  All data is delivered to stdout.\n");
309
310     exit(1);
311
312 }
313