1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  *
  31  *                      stats_create.c
  32  *
  33  * Routines for the `clean interface' to cachefs statistics.
  34  */
  35 
  36 #include <stdarg.h>
  37 #include <libintl.h>
  38 #include <sys/types.h>
  39 #include <sys/stat.h>
  40 #include <assert.h>
  41 #include <sys/fs/cachefs_fs.h>
  42 #include <string.h>
  43 #include "stats.h"
  44 
  45 void    *malloc(), *calloc();
  46 
  47 /* forward declarations of statics */
  48 static stats_cookie_t *stats_create(char *);
  49 
  50 static stats_cookie_t *
  51 stats_create(char *progname)
  52 {
  53         stats_cookie_t *rc;
  54 
  55         if ((rc = (stats_cookie_t *)calloc(1, sizeof (*rc))) == NULL)
  56                 goto out;
  57 
  58         rc->st_magic = STATS_MAGIC;
  59         if (rc->st_progname = strrchr(progname, '/'))
  60                 rc->st_progname++;
  61         else
  62                 rc->st_progname = progname;
  63 
  64         if ((rc->st_kstat_cookie = kstat_open()) == NULL) {
  65                 stats_perror(rc, SE_KERNEL,
  66                     gettext("Cannot initialize kstats"));
  67                 goto out;
  68         }
  69 
  70 out:
  71         return (rc);
  72 }
  73 
  74 stats_cookie_t *
  75 stats_create_unbound(char *progname)
  76 {
  77         stats_cookie_t *st;
  78 
  79         if ((st = stats_create(progname)) == NULL)
  80                 goto out;
  81 
  82         st->st_flags |= ST_VALID;
  83 
  84 out:
  85         return (st);
  86 }
  87 
  88 stats_cookie_t *
  89 stats_create_mountpath(char *mountpath, char *progname)
  90 {
  91         stats_cookie_t *st;
  92         kstat_t *key;
  93         cachefs_kstat_key_t *k;
  94         dev_t dev;
  95         ino64_t ino;
  96         struct stat64 s;
  97         int i, n;
  98 
  99         if ((st = stats_create(progname)) == NULL)
 100                 goto out;
 101 
 102         if ((key = kstat_lookup(st->st_kstat_cookie, "cachefs", 0, "key"))
 103             == NULL) {
 104                 stats_perror(st, SE_KERNEL,
 105                     gettext("Cannot lookup cachefs key kstat"));
 106                 goto out;
 107         }
 108         if (kstat_read(st->st_kstat_cookie, key, NULL) < 0) {
 109                 stats_perror(st, SE_KERNEL,
 110                     gettext("Cannot read cachefs key kstat"));
 111                 goto out;
 112         }
 113         k = (cachefs_kstat_key_t *)key->ks_data;
 114         n = key->ks_ndata;
 115 
 116         if (stat64(mountpath, &s) != 0) {
 117                 stats_perror(st, SE_FILE,
 118                     gettext("Cannot stat %s"), mountpath);
 119                 goto out;
 120         }
 121         ino = s.st_ino;
 122         dev = s.st_dev;
 123 
 124         for (i = 0; i < n; i++) {
 125                 k[i].ks_mountpoint += (uintptr_t)k;
 126                 k[i].ks_backfs += (uintptr_t)k;
 127                 k[i].ks_cachedir += (uintptr_t)k;
 128                 k[i].ks_cacheid += (uintptr_t)k;
 129 
 130                 if (! k[i].ks_mounted)
 131                         continue;
 132 
 133                 if ((stat64((char *)(uintptr_t)k[i].ks_mountpoint, &s) == 0) &&
 134                     (s.st_dev == dev) &&
 135                     (s.st_ino == ino))
 136                         break;
 137         }
 138 
 139         if (i >= n) {
 140                 stats_perror(st, SE_FILE,
 141                     gettext("%s: not a cachefs mountpoint"), mountpath);
 142                 goto out;
 143         }
 144 
 145         st->st_fsid = k[i].ks_id;
 146 
 147         st->st_flags |= ST_VALID | ST_BOUND;
 148 
 149 out:
 150         return (st);
 151 }
 152 
 153 /*
 154  * stats_next - bind the cookie to the next valid cachefs mount.
 155  *
 156  * returns cachefs_kstat_key_t *, which gives all the info you need.
 157  * returns NULL if we're out of mounts, or if an error occured.
 158  * returns malloc()ed data, which the client has to free() itself.
 159  */
 160 
 161 cachefs_kstat_key_t *
 162 stats_next(stats_cookie_t *st)
 163 {
 164         kstat_t *key;
 165         cachefs_kstat_key_t *k, *prc = NULL, *rc = NULL;
 166         int i, n;
 167 
 168         assert(stats_good(st));
 169 
 170         if (((key = kstat_lookup(st->st_kstat_cookie, "cachefs", 0,
 171             "key")) == NULL) ||
 172             (kstat_read(st->st_kstat_cookie, key, NULL) < 0)) {
 173                 stats_perror(st, SE_KERNEL,
 174                     gettext("Cannot get cachefs key kstat"));
 175                 goto out;
 176         }
 177         k = (cachefs_kstat_key_t *)key->ks_data;
 178         n = key->ks_ndata;
 179 
 180         if (st->st_flags & ST_BOUND) {
 181                 for (i = 0; i < n; i++)
 182                         if (st->st_fsid == k[i].ks_id)
 183                                 break;
 184                 ++i;
 185                 if (i < n) {
 186                         prc = k + i;
 187                         st->st_fsid = k[i].ks_id;
 188                 } else
 189                         st->st_flags &= ~ST_BOUND;
 190         } else if (n > 0) {
 191                 st->st_fsid = k[0].ks_id;
 192                 st->st_flags |= ST_BOUND;
 193                 prc = k;
 194         }
 195 
 196 out:
 197         if (prc != NULL) {
 198                 char *s;
 199                 int size;
 200 
 201                 prc->ks_mountpoint += (uintptr_t)k;
 202                 prc->ks_backfs += (uintptr_t)k;
 203                 prc->ks_cachedir += (uintptr_t)k;
 204                 prc->ks_cacheid += (uintptr_t)k;
 205 
 206                 size = sizeof (*rc);
 207                 size += strlen((char *)(uintptr_t)prc->ks_mountpoint) + 1;
 208                 size += strlen((char *)(uintptr_t)prc->ks_backfs) + 1;
 209                 size += strlen((char *)(uintptr_t)prc->ks_cachedir) + 1;
 210                 size += strlen((char *)(uintptr_t)prc->ks_cacheid) + 1;
 211 
 212                 if ((rc = (cachefs_kstat_key_t *)
 213                     malloc(size)) == NULL) {
 214                         stats_perror(st, SE_NOMEM,
 215                             gettext("Cannot malloc return code"));
 216                 } else {
 217                         memcpy(rc, prc, sizeof (*rc));
 218                         s = (char *)((uintptr_t)rc + sizeof (*rc));
 219 
 220                         (void) strcpy(s, (char *)(uintptr_t)prc->ks_mountpoint);
 221                         rc->ks_mountpoint = (uintptr_t)s;
 222                         s += strlen(s) + 1;
 223                         (void) strcpy(s, (char *)(uintptr_t)prc->ks_backfs);
 224                         rc->ks_backfs = (uintptr_t)s;
 225                         s += strlen(s) + 1;
 226                         (void) strcpy(s, (char *)(uintptr_t)prc->ks_cachedir);
 227                         rc->ks_cachedir = (uintptr_t)s;
 228                         s += strlen(s) + 1;
 229                         (void) strcpy(s, (char *)(uintptr_t)prc->ks_cacheid);
 230                         rc->ks_cacheid = (uintptr_t)s;
 231                 }
 232         }
 233 
 234         return (rc);
 235 }
 236 
 237 cachefs_kstat_key_t *
 238 stats_getkey(stats_cookie_t *st)
 239 {
 240         kstat_t *ksp;
 241         cachefs_kstat_key_t *k, *key, *rc = NULL;
 242         int size;
 243         char *s;
 244 
 245         assert(stats_good(st));
 246         assert(st->st_flags & ST_BOUND);
 247 
 248         if (((ksp = kstat_lookup(st->st_kstat_cookie, "cachefs", 0,
 249             "key")) == NULL) ||
 250             (kstat_read(st->st_kstat_cookie, ksp, NULL) < 0)) {
 251                 stats_perror(st, SE_KERNEL,
 252                     gettext("Cannot get cachefs key kstat"));
 253                 goto out;
 254         }
 255         key = (cachefs_kstat_key_t *)ksp->ks_data;
 256         k = key + st->st_fsid - 1;
 257         k->ks_mountpoint += (uintptr_t)key;
 258         k->ks_backfs += (uintptr_t)key;
 259         k->ks_cachedir += (uintptr_t)key;
 260         k->ks_cacheid += (uintptr_t)key;
 261         size = sizeof (*rc);
 262         size += strlen((char *)(uintptr_t)k->ks_mountpoint) + 1;
 263         size += strlen((char *)(uintptr_t)k->ks_backfs) + 1;
 264         size += strlen((char *)(uintptr_t)k->ks_cachedir) + 1;
 265         size += strlen((char *)(uintptr_t)k->ks_cacheid) + 1;
 266 
 267         if ((rc = (cachefs_kstat_key_t *)malloc(size)) == NULL)
 268                 stats_perror(st, SE_NOMEM,
 269                     gettext("Cannot malloc return code"));
 270         else {
 271                 memcpy(rc, k, sizeof (*rc));
 272                 s = (char *)((uintptr_t)rc + sizeof (*rc));
 273 
 274                 (void) strcpy(s, (char *)(uintptr_t)k->ks_mountpoint);
 275                 rc->ks_mountpoint = (uintptr_t)s;
 276                 s += strlen(s) + 1;
 277                 (void) strcpy(s, (char *)(uintptr_t)k->ks_backfs);
 278                 rc->ks_backfs = (uintptr_t)s;
 279                 s += strlen(s) + 1;
 280                 (void) strcpy(s, (char *)(uintptr_t)k->ks_cachedir);
 281                 rc->ks_cachedir = (uintptr_t)s;
 282                 s += strlen(s) + 1;
 283                 (void) strcpy(s, (char *)(uintptr_t)k->ks_cacheid);
 284                 rc->ks_cacheid = (uintptr_t)s;
 285                 s += strlen(s) + 1;
 286         }
 287 
 288         assert(rc->ks_id == st->st_fsid);
 289 
 290 out:
 291         return (rc);
 292 }
 293 
 294 void
 295 stats_destroy(stats_cookie_t *st)
 296 {
 297         void free();
 298 
 299         if (st == NULL)
 300                 return;
 301 
 302         if (st->st_kstat_cookie != NULL)
 303                 kstat_close(st->st_kstat_cookie);
 304         if (st->st_logxdr.x_ops != NULL)
 305                 xdr_destroy(&st->st_logxdr);
 306         if ((st->st_logstream != NULL) && (st->st_flags & ST_LFOPEN))
 307                 (void) fclose(st->st_logstream);
 308 
 309         /*
 310          * we don't want to depend on dbm (or stats_dbm), so we don't
 311          * do a stats_dbm_close.  we do try to require the client to
 312          * have done it, via an assert(), however.
 313          */
 314 
 315         assert(! (st->st_flags & ST_DBMOPEN));
 316 
 317         st->st_magic++;
 318 
 319         free(st);
 320 }
 321 
 322 int
 323 stats_good(stats_cookie_t *st)
 324 {
 325         if (st == NULL)
 326                 return (0);
 327         if (st->st_magic != STATS_MAGIC)
 328                 return (0);
 329         if (! (st->st_flags & ST_VALID))
 330                 return (0);
 331 
 332         return (1);
 333 }
 334 
 335 void
 336 /*PRINTFLIKE3*/
 337 stats_perror(stats_cookie_t *st, int Errno, char *fmt, ...)
 338 {
 339 
 340         va_list ap;
 341 
 342         assert(st != NULL);
 343         assert(st->st_magic == STATS_MAGIC);
 344 
 345         va_start(ap, fmt);
 346         (void) vsnprintf(st->st_errorstr, sizeof (st->st_errorstr), fmt, ap);
 347         va_end(ap);
 348 
 349         st->st_errno = Errno;
 350 
 351         st->st_flags |= ST_ERROR;
 352 }
 353 
 354 char *
 355 stats_errorstr(stats_cookie_t *st)
 356 {
 357         assert(st != NULL);
 358         assert(st->st_magic == STATS_MAGIC);
 359 
 360         return (st->st_errorstr);
 361 }
 362 
 363 int
 364 stats_errno(stats_cookie_t *st)
 365 {
 366         assert(st != NULL);
 367         assert(st->st_magic == STATS_MAGIC);
 368 
 369         return (st->st_errno);
 370 }
 371 
 372 int
 373 stats_inerror(stats_cookie_t *st)
 374 {
 375         assert(st != NULL);
 376         assert(st->st_magic == STATS_MAGIC);
 377 
 378         return (st->st_flags & ST_ERROR);
 379 }