cfsadmin.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
*
* Cache FS admin utility.
*/
#include <assert.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <ftw.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdarg.h>
#include <priv.h>
#include "../common/cachefsd.h"
char *cfsadmin_opts[] = {
#define COPT_MAXBLOCKS 0
"maxblocks",
#define COPT_MINBLOCKS 1
"minblocks",
#define COPT_THRESHBLOCKS 2
"threshblocks",
#define COPT_MAXFILES 3
"maxfiles",
#define COPT_MINFILES 4
"minfiles",
#define COPT_THRESHFILES 5
"threshfiles",
#define COPT_MAXFILESIZE 6
"maxfilesize",
#define COPT_HIBLOCKS 7
"hiblocks",
#define COPT_LOWBLOCKS 8
"lowblocks",
#define COPT_HIFILES 9
"hifiles",
#define COPT_LOWFILES 10
"lowfiles",
};
/* numbers must be valid percentages ranging from 0 to 100 */
#define badpercent(val) \
/* forward references */
int cache_stats(char *dirp);
int resource_file_dirty(char *dirp);
/*
*
* main
*
* Description:
* Main routine for the cfsadmin program.
* Arguments:
* argc number of command line arguments
* argv command line arguments
* Returns:
* Returns 0 for failure, > 0 for an error.
* Preconditions:
*/
int
{
int c;
int xx;
int lockid;
char *cacheid;
char *cachedir;
int cflag;
int uflag;
int dflag;
int sflag;
int allflag;
int lflag;
char *optionp;
int Cflag;
int Dflag;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/* set defaults for command line options */
cflag = 0;
uflag = 0;
dflag = 0;
sflag = 0;
allflag = 0;
lflag = 0;
Cflag = 0;
Dflag = 0;
/* parse the command line arguments */
switch (c) {
case 'c': /* create */
cflag = 1;
break;
/*
* -C and -D are undocumented calls used
* to simulate disconnection on a file system.
*/
case 'C': /* connect file system */
Cflag = 1;
break;
case 'D': /* disconnect file system */
Dflag = 1;
break;
case 'u': /* update */
uflag = 1;
break;
case 'd': /* delete */
dflag = 1;
allflag = 1;
else
break;
case 's': /* consistency on demand */
sflag = 1;
break;
case 'l': /* list cache ids */
lflag = 1;
break;
case 'o': /* options for update and create */
break;
default:
return (1);
}
}
return (1);
}
/* These options create files. We want them to be root owned */
return (1);
}
else if (lflag)
",", NULL);
else if (sflag)
return (1);
}
/* make sure cachedir is specified */
return (1);
}
} else {
/* make sure at least one mount point is specified */
return (1);
}
}
/* make sure a reasonable set of flags were specified */
/* flags are mutually exclusive, at least one must be set */
"exactly one of -c, -u, -d, -s, -l must be specified"));
return (1);
}
/* make sure -o specified with -c or -u */
return (1);
}
/* if creating a cache */
if (cflag) {
struct cachefs_user_values uv;
struct cache_label clabel;
/* get default cache paramaters */
/* parse the options if specified */
if (optionp) {
if (xx)
return (1);
}
/* verify options are reasonable */
if (xx)
return (1);
/* lock the cache directory non-shared */
if (lockid == -1) {
/* quit if could not get the lock */
return (1);
}
/* create the cache */
if (xx != 0) {
if (xx == -2) {
/* remove a partially created cache dir */
(void) cachefs_delete_all_cache(cachedir);
}
return (1);
}
}
/* else if updating resource parameters */
else if (uflag) {
/* lock the cache directory non-shared */
if (lockid == -1) {
/* quit if could not get the lock */
return (1);
}
if (xx != 0) {
return (1);
}
}
/* else if deleting a specific cacheID (or all caches) */
else if (dflag) {
/* lock the cache directory non-shared */
if (lockid == -1) {
/* quit if could not get the lock */
return (1);
}
/* if the cache is in use */
if (cachefs_inuse(cachedir)) {
"cannot be modified."), cachedir);
return (1);
}
if (allflag)
else {
/* mark resource file as dirty */
if (xx == 0)
}
if (xx != 0) {
return (1);
}
}
/* else if listing cache statistics */
else if (lflag) {
if (xx != 0)
return (1);
}
/* else if issuing a check event to cached file systems */
else if (sflag) {
}
}
/* else if simulating a disconnection */
else if (Dflag) {
}
}
/* else if connection after a simulated disconnection */
else if (Cflag) {
}
}
/* return success */
return (0);
}
/*
*
* usage
*
* Description:
* Prints a usage message for this utility.
* Arguments:
* msgp message to include with the usage message
* Returns:
* Preconditions:
* precond(msgp)
*/
void
{
"usage: cfsadmin -[cu] [-o parameter-list] cachedir\n"));
}
/*
*
* pr_err
*
* Description:
* Prints an error message to stderr.
* Arguments:
* fmt printf style format
* ... arguments for fmt
* Returns:
* Preconditions:
* precond(fmt)
*/
void
{
}
/*
*
* cfs_get_opts
*
* Description:
* Decodes cfs options specified with -o.
* Only the fields referenced by the options are modified.
* Arguments:
* oarg options from -o option
* uvp place to put options
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(oarg)
* precond(uvp)
*/
int
{
char *saveopts;
int badopt;
/* make a copy of the options because getsubopt modifies it */
return (-1);
}
/* process the options */
badopt = 0;
case COPT_MAXBLOCKS:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_MINBLOCKS:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_THRESHBLOCKS:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_MAXFILES:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_MINFILES:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_THRESHFILES:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_MAXFILESIZE:
badopt = 1;
else
break;
case COPT_HIBLOCKS:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_LOWBLOCKS:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_HIFILES:
if (badpercent(val))
badopt = 1;
else
break;
case COPT_LOWFILES:
if (badpercent(val))
badopt = 1;
else
break;
default:
/* if a bad option argument */
return (-1);
}
}
/* if a bad value for an option, display an error message */
if (badopt) {
saveopts);
}
/* free the duplicated option string */
/* return the result */
return (badopt ? -1 : 0);
}
/*
*
* update_cachelabel
*
* Description:
* Changes the parameters of the cache_label.
* If optionp is NULL then the cache_label is set to
* default values.
* Arguments:
* dirp the name of the cache directory
* optionp comma delimited options
* Returns:
* Returns 0 for success and -1 for an error.
* Preconditions:
* precond(dirp)
*/
int
{
char path[CACHEFS_XMAXPATH];
struct cache_label clabel_new;
struct cache_label clabel_orig;
int xx;
/* if the cache is in use */
if (cachefs_inuse(dirp)) {
dirp);
return (-1);
}
/* make sure we don't overwrite path */
dirp);
return (-1);
}
/* construct the pathname to the cach_label file */
/* read the current set of parameters */
if (xx == -1) {
return (-1);
}
if (xx != 0) {
return (-1);
}
/* convert the cache_label to user values */
if (xx) {
return (-1);
}
/* if options were specified */
if (optionp) {
/* start with the original values */
/* parse the options */
if (xx) {
return (-1);
}
/* verify options are reasonable */
if (xx) {
return (-1);
}
}
/* else if options where not specified, get defaults */
else {
}
/* convert user values to a cache_label */
if (xx) {
return (-1);
}
/* do not allow the cache size to shrink */
" maxblocks current %d%%, requested %d%%"),
return (-1);
}
" maxfiles current %d%% requested %d%%"),
return (-1);
}
/* write back the new values */
if (xx == -1) {
return (-1);
}
/* put the new values in the duplicate cache label file also */
if (xx == -1) {
return (-1);
}
/* grow resouces file if necessary */
xx = 0;
}
/* return status */
return (xx);
}
/*
*
* user_values_defaults
*
* Description:
* Sets default values in the cachefs_user_values object.
* Arguments:
* uvp cachefs_user_values object to set values for
* Returns:
* Preconditions:
* precond(uvp)
*/
void
{
uvp->uv_minblocks = 0;
uvp->uv_minfiles = 0;
}
/*
*
* check_user_values_for_sanity
*
* Description:
* Check the cachefs_user_values for sanity.
* Arguments:
* uvp cachefs_user_values object to check
* Returns:
* Returns 0 if okay, -1 if not.
* Preconditions:
* precond(uvp)
*/
int
{
int ret;
ret = 0;
ret = -1;
}
ret = -1;
}
/* XXX more conditions to check here? */
/* XXX make sure thresh values are between min and max values */
/* return status */
return (ret);
}
/*
*
* cache_stats
*
* Description:
* Show each cache in the directory, cache resource statistics,
* and, for each fs in the cache, the name of the fs, and the
* cache resource parameters.
* Arguments:
* dirp name of the cache directory
* Returns:
* Returns 0 for success, -1 for an error.
* Errors:
* Preconditions:
*/
int
cache_stats(char *dirp)
{
char path[CACHEFS_XMAXPATH];
int ret;
int xx;
struct cache_label clabel;
struct cachefs_user_values uv;
/* make sure cache dir name is not too long */
return (-1);
}
/* read the cache label file */
if (xx == -1) {
return (-1);
}
if (xx != 0) {
return (-1);
}
/* convert the cache_label to user values */
if (xx)
return (-1);
/* display the parameters */
#if 0
#endif
/* open the directory */
return (-1);
}
/* loop reading the contents of the directory */
ret = 0;
/* ignore . and .. */
continue;
/* stat the file */
if (xx == -1) {
return (-1);
}
/* ignore anything that is not a link */
continue;
/* print the file system cache directory name */
/* XXX anything else */
}
/* XXX what about stats */
/* return status */
return (ret);
}
/*
*
* resource_file_grow
*
* Description:
* Grows the resource file in the specified directory
* to its new size.
* Arguments:
* dirp cache directory resource file is in
* oldcnt previous number of files in resource file
* newcnt new number of files in resource file
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(dirp)
* precond(oldcnt <= newcnt)
* precond(cache is locked exclusively)
* precond(cache is not in use)
*/
int
{
int fd;
char path[CACHEFS_XMAXPATH];
int xx;
int cnt;
int dirty;
/* get info about the resouce file for the various sizes */
/* open the resource file for writing */
/* this file is < 2GB */
if (fd == -1) {
return (-1);
}
/* get info on the file */
if (xx == -1) {
return (-1);
}
/* make sure the size is the correct */
return (-1);
}
/* read the cache usage structure */
xx);
return (-1);
}
/* rewind */
if (xx == -1) {
return (-1);
}
/* indicate cache is dirty if necessary */
dirty = 1;
dirty = 0;
"Could not write cache_usage, %d, run fsck"),
xx);
return (-1);
}
}
/* go to the end of the file */
if (xx == -1) {
return (-1);
}
/* grow the file to the new size */
while (cnt-- > 0) {
xx);
return (-1);
}
}
/* mmap the file into our address space */
fd, 0);
if (addrp == (void *)-1) {
return (-1);
}
/* close the file descriptor, we do not need it anymore */
/* move the idents region to its new location */
/* zero out the old idents region that is now in the pointers region */
/* sync the data to the file */
if (xx == -1) {
return (-1);
}
/* mark the file as clean if it was not dirty originally */
if (!dirty) {
/* sync the data to the file */
if (xx == -1) {
return (-1);
}
}
/* unmap the file */
/* return success */
return (0);
}
/*
*
* resource_file_dirty
*
* Description:
* Marks the resource file as dirty.
* This will cause fsck to fix it up the next time it
* is run.
* Arguments:
* dirp cache directory resource file is in
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(dirp)
* precond(cache is locked exclusively)
* precond(cache is not in use)
*/
int
resource_file_dirty(char *dirp)
{
int fd;
char path[CACHEFS_XMAXPATH];
int xx;
struct cache_usage cusage;
/* open the resource file for writing */
/* this file is < 2GB */
if (fd == -1) {
return (-1);
}
/* read the cache usage structure */
xx);
return (-1);
}
/* rewind */
if (xx == -1) {
return (-1);
}
/* indicate cache is dirty if necessary */
"Could not write cache_usage, %d, run fsck"),
xx);
return (-1);
}
}
if (xx == -1) {
}
return (xx);
}
/*
*
* issue_cod
*
* Description:
* Executes the _FIOCOD ioctl on the specified file.
* Arguments:
* name filename to issue ioctl on (or "all")
* Returns:
* Returns 0 for success, -1 for an error.
* Preconditions:
* precond(dirp)
*/
int
{
int fd;
int xx;
int arg;
char *dirp;
#ifndef MNTTYPE_CACHEFS
#define MNTTYPE_CACHEFS "cachefs"
#endif
arg = 0;
/*
* if "all" was specified rather than a mount point,
* mount will do). We issue the ioctl on this mount point,
* and specify a non-zero argument to the ioctl. The non-zero
* arg tells the kernel to do demandconst on all relevant
* cachefs mounts
*/
return (-1);
}
return (-1);
}
arg = 1;
} else {
}
/* open the file */
if (fd == -1) {
return (-1);
}
/* issue the ioctl */
if (xx) {
dirp);
if (arg == 0)
/* we're quiet if "all" was specified */
" mounted demandconst."), dirp);
} else {
}
}
return (xx);
}
/*
*
* simulate_disconnection
*
* Description:
* Sends the rpc message to the cachefsd to turn simulated
* disconnection on or off
* Arguments:
* namep name of file system or "all"
* disconnect 1 means disconnect, 0 means connect
* Returns:
* Preconditions:
* precond(name)
*/
void
{
int ret;
int xx;
int result;
char *hostp;
struct cachefsd_disconnection_args args;
char *msgp;
/* get the host name */
if (xx == -1) {
return;
}
/* creat the connection to the daemon */
return;
}
/* give it a chance to complete */
/* perform the operation */
if (retval != RPC_SUCCESS) {
return;
}
/* check for error from daemon */
if (ret != 0) {
if (disconnect) {
switch (ret) {
default:
msgp = "unknown error";
break;
case 1:
msgp = "not mounted disconnectable";
break;
case 2:
msgp = "already disconnected";
break;
case 3:
msgp = "not a cached file system";
break;
}
} else {
switch (ret) {
default:
msgp = "unknown error";
break;
case 1:
msgp = "already connected";
break;
case 2:
msgp = "not simulated disconnection";
break;
case 3:
msgp = "not a cached file system";
break;
}
}
}
ret = 0;
}