freebsd-skq/sbin/ggate/shared/ggate.c
2010-02-18 23:04:01 +00:00

410 lines
9.2 KiB
C

/*-
* Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/stat.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <signal.h>
#include <err.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <libgen.h>
#include <libutil.h>
#include <netdb.h>
#include <syslog.h>
#include <stdarg.h>
#include <stdint.h>
#include <libgeom.h>
#include <geom/gate/g_gate.h>
#include "ggate.h"
int g_gate_devfd = -1;
int g_gate_verbose = 0;
void
g_gate_vlog(int priority, const char *message, va_list ap)
{
if (g_gate_verbose) {
const char *prefix;
switch (priority) {
case LOG_ERR:
prefix = "error";
break;
case LOG_WARNING:
prefix = "warning";
break;
case LOG_NOTICE:
prefix = "notice";
break;
case LOG_INFO:
prefix = "info";
break;
case LOG_DEBUG:
prefix = "debug";
break;
default:
prefix = "unknown";
}
printf("%s: ", prefix);
vprintf(message, ap);
printf("\n");
} else {
if (priority != LOG_DEBUG)
vsyslog(priority, message, ap);
}
}
void
g_gate_log(int priority, const char *message, ...)
{
va_list ap;
va_start(ap, message);
g_gate_vlog(priority, message, ap);
va_end(ap);
}
void
g_gate_xvlog(const char *message, va_list ap)
{
g_gate_vlog(LOG_ERR, message, ap);
g_gate_vlog(LOG_ERR, "Exiting.", ap);
exit(EXIT_FAILURE);
}
void
g_gate_xlog(const char *message, ...)
{
va_list ap;
va_start(ap, message);
g_gate_xvlog(message, ap);
/* NOTREACHED */
va_end(ap);
exit(EXIT_FAILURE);
}
off_t
g_gate_mediasize(int fd)
{
off_t mediasize;
struct stat sb;
if (fstat(fd, &sb) == -1)
g_gate_xlog("fstat(): %s.", strerror(errno));
if (S_ISCHR(sb.st_mode)) {
if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
g_gate_xlog("Can't get media size: %s.",
strerror(errno));
}
} else if (S_ISREG(sb.st_mode)) {
mediasize = sb.st_size;
} else {
g_gate_xlog("Unsupported file system object.");
}
return (mediasize);
}
unsigned
g_gate_sectorsize(int fd)
{
unsigned secsize;
struct stat sb;
if (fstat(fd, &sb) == -1)
g_gate_xlog("fstat(): %s.", strerror(errno));
if (S_ISCHR(sb.st_mode)) {
if (ioctl(fd, DIOCGSECTORSIZE, &secsize) == -1) {
g_gate_xlog("Can't get sector size: %s.",
strerror(errno));
}
} else if (S_ISREG(sb.st_mode)) {
secsize = 512;
} else {
g_gate_xlog("Unsupported file system object.");
}
return (secsize);
}
void
g_gate_open_device(void)
{
g_gate_devfd = open("/dev/" G_GATE_CTL_NAME, O_RDWR);
if (g_gate_devfd == -1)
err(EXIT_FAILURE, "open(/dev/%s)", G_GATE_CTL_NAME);
}
void
g_gate_close_device(void)
{
close(g_gate_devfd);
}
void
g_gate_ioctl(unsigned long req, void *data)
{
if (ioctl(g_gate_devfd, req, data) == -1) {
g_gate_xlog("%s: ioctl(/dev/%s): %s.", getprogname(),
G_GATE_CTL_NAME, strerror(errno));
}
}
void
g_gate_destroy(int unit, int force)
{
struct g_gate_ctl_destroy ggio;
ggio.gctl_version = G_GATE_VERSION;
ggio.gctl_unit = unit;
ggio.gctl_force = force;
g_gate_ioctl(G_GATE_CMD_DESTROY, &ggio);
}
void
g_gate_load_module(void)
{
if (modfind("g_gate") == -1) {
/* Not present in kernel, try loading it. */
if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
if (errno != EEXIST) {
errx(EXIT_FAILURE,
"geom_gate module not available!");
}
}
}
}
/*
* When we send from ggatec packets larger than 32kB, performance drops
* significantly (eg. to 256kB/s over 1Gbit/s link). This is not a problem
* when data is send from ggated. I don't know why, so for now I limit
* size of packets send from ggatec to 32kB by defining MAX_SEND_SIZE
* in ggatec Makefile.
*/
#ifndef MAX_SEND_SIZE
#define MAX_SEND_SIZE MAXPHYS
#endif
ssize_t
g_gate_send(int s, const void *buf, size_t len, int flags)
{
ssize_t done = 0, done2;
const unsigned char *p = buf;
while (len > 0) {
done2 = send(s, p, MIN(len, MAX_SEND_SIZE), flags);
if (done2 == 0)
break;
else if (done2 == -1) {
if (errno == EAGAIN) {
printf("%s: EAGAIN\n", __func__);
continue;
}
done = -1;
break;
}
done += done2;
p += done2;
len -= done2;
}
return (done);
}
ssize_t
g_gate_recv(int s, void *buf, size_t len, int flags)
{
ssize_t done;
do {
done = recv(s, buf, len, flags);
} while (done == -1 && errno == EAGAIN);
return (done);
}
int nagle = 1;
unsigned rcvbuf = G_GATE_RCVBUF;
unsigned sndbuf = G_GATE_SNDBUF;
void
g_gate_socket_settings(int sfd)
{
struct timeval tv;
int bsize, on;
/* Socket settings. */
on = 1;
if (nagle) {
if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on,
sizeof(on)) == -1) {
g_gate_xlog("setsockopt() error: %s.", strerror(errno));
}
}
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
g_gate_xlog("setsockopt(SO_REUSEADDR): %s.", strerror(errno));
bsize = rcvbuf;
if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == -1)
g_gate_xlog("setsockopt(SO_RCVBUF): %s.", strerror(errno));
bsize = sndbuf;
if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)) == -1)
g_gate_xlog("setsockopt(SO_SNDBUF): %s.", strerror(errno));
tv.tv_sec = 8;
tv.tv_usec = 0;
if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
g_gate_log(LOG_ERR, "setsockopt(SO_SNDTIMEO) error: %s.",
strerror(errno));
}
if (setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
g_gate_log(LOG_ERR, "setsockopt(SO_RCVTIMEO) error: %s.",
strerror(errno));
}
}
#ifdef LIBGEOM
static struct gclass *
find_class(struct gmesh *mesh, const char *name)
{
struct gclass *class;
LIST_FOREACH(class, &mesh->lg_class, lg_class) {
if (strcmp(class->lg_name, name) == 0)
return (class);
}
return (NULL);
}
static const char *
get_conf(struct ggeom *gp, const char *name)
{
struct gconfig *conf;
LIST_FOREACH(conf, &gp->lg_config, lg_config) {
if (strcmp(conf->lg_name, name) == 0)
return (conf->lg_val);
}
return (NULL);
}
static void
show_config(struct ggeom *gp, int verbose)
{
struct gprovider *pp;
char buf[5];
pp = LIST_FIRST(&gp->lg_provider);
if (pp == NULL)
return;
if (!verbose) {
printf("%s\n", pp->lg_name);
return;
}
printf(" NAME: %s\n", pp->lg_name);
printf(" info: %s\n", get_conf(gp, "info"));
printf(" access: %s\n", get_conf(gp, "access"));
printf(" timeout: %s\n", get_conf(gp, "timeout"));
printf("queue_count: %s\n", get_conf(gp, "queue_count"));
printf(" queue_size: %s\n", get_conf(gp, "queue_size"));
printf(" references: %s\n", get_conf(gp, "ref"));
humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
printf(" mediasize: %jd (%s)\n", (intmax_t)pp->lg_mediasize, buf);
printf(" sectorsize: %u\n", pp->lg_sectorsize);
printf(" mode: %s\n", pp->lg_mode);
printf("\n");
}
void
g_gate_list(int unit, int verbose)
{
struct gmesh mesh;
struct gclass *class;
struct ggeom *gp;
char name[64];
int error;
error = geom_gettree(&mesh);
if (error != 0)
exit(EXIT_FAILURE);
class = find_class(&mesh, G_GATE_CLASS_NAME);
if (class == NULL) {
geom_deletetree(&mesh);
exit(EXIT_SUCCESS);
}
if (unit >= 0) {
snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME,
unit);
}
LIST_FOREACH(gp, &class->lg_geom, lg_geom) {
if (unit != -1 && strcmp(gp->lg_name, name) != 0)
continue;
show_config(gp, verbose);
}
geom_deletetree(&mesh);
exit(EXIT_SUCCESS);
}
#endif /* LIBGEOM */
in_addr_t
g_gate_str2ip(const char *str)
{
struct hostent *hp;
in_addr_t ip;
ip = inet_addr(str);
if (ip != INADDR_NONE) {
/* It is a valid IP address. */
return (ip);
}
/* Check if it is a valid host name. */
hp = gethostbyname(str);
if (hp == NULL)
return (INADDR_NONE);
return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
}