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*****************************************/ 58void show_usage(void); 59void dump_cache_stats(void); 60void dump_cache_users(void); 61char *make_time(time_t); 62 63/**************************************** 64* * module globals 65*****************************************/ 66static void *shm_base = NULL; 67static struct bucket *table = NULL; 68static struct stats *table_stats = NULL; 69 70/**************************************** 71*****************************************/ 72 73 74/************************************************************* 75* * Main 76**************************************************************/ 77int 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****************************************************/ 159void 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****************************************************/ 187void 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****************************************************/ 277char *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****************************************************/ 298void 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 314