blob: 1b64bd7fb1c9d4267ff88646b5836f4335b4bac4 [file] [log] [blame]
/*
* error.c -- Common error handling functions
*
* Copyright (C) 2007 Oracle. All rights reserved.
* Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*
* To Do:
* + Proper support for internationalization
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <rpc/rpc.h>
#include "xcommon.h"
#include "nls.h"
#include "mount.h"
#include "error.h"
#ifdef HAVE_RPCSVC_NFS_PROT_H
#include <rpcsvc/nfs_prot.h>
#else
#include <linux/nfs.h>
#define nfsstat nfs_stat
#endif
extern char *progname;
static char errbuf[BUFSIZ];
static char *erreob = &errbuf[BUFSIZ];
/* Convert RPC errors into strings */
static int rpc_strerror(int spos)
{
int cf_stat = rpc_createerr.cf_stat;
int pos = 0, cf_errno = rpc_createerr.cf_error.re_errno;
char *ptr, *estr = clnt_sperrno(cf_stat);
char *tmp;
if (estr) {
if ((ptr = index(estr, ':')))
estr = ++ptr;
tmp = &errbuf[spos];
if (cf_stat == RPC_SYSTEMERROR)
pos = snprintf(tmp, (erreob - tmp),
_("System Error: %s"),
strerror(cf_errno));
else {
if (cf_errno)
pos = snprintf(tmp, (erreob - tmp),
_("RPC Error:%s; errno = %s"),
estr, strerror(cf_errno));
else
pos = snprintf(tmp, (erreob - tmp),
_("RPC Error:%s"), estr);
}
}
return pos;
}
/**
* rpc_mount_errors - log an RPC error that occurred during a user-space mount
* @server: C string containing name of server we are attempting to mount
* @will_retry: one indicates mount will retry at some later point
* @bg: one indicates this is a background mount
*
* Extracts the error code from the user-space RPC library, and reports it
* on stderr (fg mount) or in the system log (bg mount).
*/
void rpc_mount_errors(char *server, int will_retry, int bg)
{
int pos = 0;
char *tmp;
static int onlyonce = 0;
tmp = &errbuf[pos];
if (bg)
pos = snprintf(tmp, (erreob - tmp),
_("mount to NFS server '%s' failed: "),
server);
else
pos = snprintf(tmp, (erreob - tmp),
_("%s: mount to NFS server '%s' failed: "),
progname, server);
tmp = &errbuf[pos];
if (rpc_createerr.cf_stat == RPC_TIMEDOUT) {
if (will_retry)
pos = snprintf(tmp, (erreob - tmp),
_("timed out, retrying"));
else
pos = snprintf(tmp, (erreob - tmp),
_("timed out, giving up"));
} else {
pos += rpc_strerror(pos);
tmp = &errbuf[pos];
if (bg) {
if (will_retry)
pos = snprintf(tmp, (erreob - tmp),
_(", retrying"));
else
pos = snprintf(tmp, (erreob - tmp),
_(", giving up"));
}
}
if (bg) {
if (onlyonce++ < 1)
openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
syslog(LOG_ERR, "%s", errbuf);
} else
fprintf(stderr, "%s\n", errbuf);
}
/**
* sys_mount_errors - log an error that occurred during a mount system call
* @server: C string containing name of server we are attempting to mount
* @error: errno value to report
* @will_retry: one indicates mount will retry at some later point
* @bg: one indicates this is a background mount
*
* Passed an errno value generated by a mount system call, and reports it
* on stderr (fg mount) or in the system log (bg mount).
*/
void sys_mount_errors(char *server, int error, int will_retry, int bg)
{
int pos = 0;
char *tmp;
static int onlyonce = 0;
tmp = &errbuf[pos];
if (bg)
pos = snprintf(tmp, (erreob - tmp),
_("mount to NFS server '%s' failed: "),
server);
else
pos = snprintf(tmp, (erreob - tmp),
_("%s: mount to NFS server '%s' failed: "),
progname, server);
tmp = &errbuf[pos];
if (error == ETIMEDOUT) {
if (will_retry)
pos = snprintf(tmp, (erreob - tmp),
_("timed out, retrying"));
else
pos = snprintf(tmp, (erreob - tmp),
_("timed out, giving up"));
} else {
if (bg) {
if (will_retry)
pos = snprintf(tmp, (erreob - tmp),
_("%s, retrying"),
strerror(error));
else
pos = snprintf(tmp, (erreob - tmp),
_("%s, giving up"),
strerror(error));
}
}
if (bg) {
if (onlyonce++ < 1)
openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
syslog(LOG_ERR, "%s", errbuf);
} else
fprintf(stderr, "%s\n", errbuf);
}
/**
* mount_error - report a foreground mount error
* @spec: C string containing the device name being mounted
* @mount_point: C string containing the pathname of the local mounted on dir
* @error: errno value to report
*
*/
void mount_error(const char *spec, const char *mount_point, int error)
{
switch(error) {
case EACCES:
nfs_error(_("%s: access denied by server while mounting %s"),
progname, spec);
break;
case EINVAL:
nfs_error(_("%s: an incorrect mount option was specified"), progname);
break;
case EOPNOTSUPP:
nfs_error(_("%s: requested NFS version or transport"
" protocol is not supported"),
progname);
break;
case ENOTDIR:
nfs_error(_("%s: mount point %s is not a directory"),
progname, mount_point);
break;
case EBUSY:
nfs_error(_("%s: %s is busy or already mounted"),
progname, mount_point);
break;
case ENOENT:
if (spec)
nfs_error(_("%s: mounting %s failed, "
"reason given by server:\n %s"),
progname, spec, strerror(error));
else
nfs_error(_("%s: mount point %s does not exist"),
progname, mount_point);
break;
case ESPIPE:
rpc_mount_errors((char *)spec, 0, 0);
break;
case EIO:
nfs_error(_("%s: mount system call failed"), progname);
break;
case EFAULT:
nfs_error(_("%s: encountered unexpected error condition."),
progname);
nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT),
progname);
break;
default:
nfs_error(_("%s: %s"),
progname, strerror(error));
}
}
/*
* umount_error - report a failed umount request
* @err: errno value to report
* @dev: C string containing the pathname of the local mounted on dir
*
*/
void umount_error(int err, const char *dev)
{
switch (err) {
case ENXIO:
nfs_error(_("%s: %s: invalid block device"),
progname, dev);
break;
case EINVAL:
nfs_error(_("%s: %s: not mounted"),
progname, dev);
break;
case EIO:
nfs_error(_("%s: %s: can't write superblock"),
progname, dev);
break;
case EBUSY:
nfs_error(_("%s: %s: device is busy"),
progname, dev);
break;
case ENOENT:
nfs_error(_("%s: %s: not found"),
progname, dev);
break;
case EPERM:
nfs_error(_("%s: %s: must be superuser to umount"),
progname, dev);
break;
case EACCES:
nfs_error(_("%s: %s: block devices not permitted on fs"),
progname, dev);
break;
default:
nfs_error(_("%s: %s: %s"),
progname, dev, strerror(err));
break;
}
}
/*
* We need to translate between nfs status return values and
* the local errno values which may not be the same.
*
* Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
* "after #include <errno.h> the symbol errno is reserved for any use,
* it cannot even be used as a struct tag or field name".
*/
#ifndef EDQUOT
#define EDQUOT ENOSPC
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static struct {
enum nfsstat stat;
int errnum;
} nfs_errtbl[] = {
{ NFS_OK, 0 },
{ NFSERR_PERM, EPERM },
{ NFSERR_NOENT, ENOENT },
{ NFSERR_IO, EIO },
{ NFSERR_NXIO, ENXIO },
{ NFSERR_ACCES, EACCES },
{ NFSERR_EXIST, EEXIST },
{ NFSERR_NODEV, ENODEV },
{ NFSERR_NOTDIR, ENOTDIR },
{ NFSERR_ISDIR, EISDIR },
#ifdef NFSERR_INVAL
{ NFSERR_INVAL, EINVAL }, /* that Sun forgot */
#endif
{ NFSERR_FBIG, EFBIG },
{ NFSERR_NOSPC, ENOSPC },
{ NFSERR_ROFS, EROFS },
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
{ NFSERR_NOTEMPTY, ENOTEMPTY },
{ NFSERR_DQUOT, EDQUOT },
{ NFSERR_STALE, ESTALE },
#ifdef EWFLUSH
{ NFSERR_WFLUSH, EWFLUSH },
#endif
/* Throw in some NFSv3 values for even more fun (HP returns these) */
{ 71, EREMOTE },
};
char *nfs_strerror(unsigned int stat)
{
unsigned int i;
static char buf[256];
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].stat == stat)
return strerror(nfs_errtbl[i].errnum);
}
sprintf(buf, _("unknown nfs status return value: %u"), stat);
return buf;
}