From aff33d2e1fd7b4c8d91ff0cc5ea6ec4da125e1e0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 26 Apr 2012 11:22:46 -0400 Subject: [PATCH] nfsdcld: add client tracking daemon stub This program opens and "listens" on the new nfsd/cld rpc_pipefs pipe. The code here doesn't actually do anything on stable storage yet. That will be added in a later patch. The patch also adds a autoconf enable switch for the new daemon that defaults to "no", and a test for the upcall description header file. Signed-off-by: Jeff Layton Signed-off-by: Steve Dickson --- configure.ac | 9 ++ support/include/cld.h | 56 ++++++++ utils/Makefile.am | 4 + utils/nfsdcld/Makefile.am | 14 ++ utils/nfsdcld/nfsdcld.c | 274 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 357 insertions(+) create mode 100644 support/include/cld.h create mode 100644 utils/nfsdcld/Makefile.am create mode 100644 utils/nfsdcld/nfsdcld.c diff --git a/configure.ac b/configure.ac index 67e8d2d..a9ffb68 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,12 @@ else AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mount" = "yes"]) fi +AC_ARG_ENABLE(nfsdcld, + [AC_HELP_STRING([--enable-nfsdcld], + [Create nfsdcld NFSv4 clientid tracking daemon. @<:@default=no@:>@])], + enable_nfsdcld=$enableval, + enable_nfsdcld="no") + dnl Check for TI-RPC library and headers AC_LIBTIRPC @@ -260,6 +266,8 @@ if test "$enable_nfsv4" = yes; then dnl check for the keyutils libraries and headers AC_KEYUTILS + AM_CONDITIONAL(CONFIG_NFSDCLD, [test "$enable_nfsdcld" = "yes" ]) + dnl librpcsecgss already has a dependency on libgssapi, dnl but we need to make sure we get the right version if test "$enable_gss" = yes; then @@ -457,6 +465,7 @@ AC_CONFIG_FILES([ tools/nfs-iostat/Makefile utils/Makefile utils/blkmapd/Makefile + utils/nfsdcld/Makefile utils/exportfs/Makefile utils/gssd/Makefile utils/idmapd/Makefile diff --git a/support/include/cld.h b/support/include/cld.h new file mode 100644 index 0000000..f14a9ab --- /dev/null +++ b/support/include/cld.h @@ -0,0 +1,56 @@ +/* + * Upcall description for nfsdcld communication + * + * Copyright (c) 2012 Red Hat, Inc. + * Author(s): Jeff Layton + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _NFSD_CLD_H +#define _NFSD_CLD_H + +/* latest upcall version available */ +#define CLD_UPCALL_VERSION 1 + +/* defined by RFC3530 */ +#define NFS4_OPAQUE_LIMIT 1024 + +enum cld_command { + Cld_Create, /* create a record for this cm_id */ + Cld_Remove, /* remove record of this cm_id */ + Cld_Check, /* is this cm_id allowed? */ + Cld_GraceDone, /* grace period is complete */ +}; + +/* representation of long-form NFSv4 client ID */ +struct cld_name { + uint16_t cn_len; /* length of cm_id */ + unsigned char cn_id[NFS4_OPAQUE_LIMIT]; /* client-provided */ +} __attribute__((packed)); + +/* message struct for communication with userspace */ +struct cld_msg { + uint8_t cm_vers; /* upcall version */ + uint8_t cm_cmd; /* upcall command */ + int16_t cm_status; /* return code */ + uint32_t cm_xid; /* transaction id */ + union { + int64_t cm_gracetime; /* grace period start time */ + struct cld_name cm_name; + } __attribute__((packed)) cm_u; +} __attribute__((packed)); + +#endif /* !_NFSD_CLD_H */ diff --git a/utils/Makefile.am b/utils/Makefile.am index d074b85..5df7ca7 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -21,6 +21,10 @@ if CONFIG_MOUNT OPTDIRS += mount endif +if CONFIG_NFSDCLD +OPTDIRS += nfsdcld +endif + SUBDIRS = \ exportfs \ mountd \ diff --git a/utils/nfsdcld/Makefile.am b/utils/nfsdcld/Makefile.am new file mode 100644 index 0000000..ed7ed42 --- /dev/null +++ b/utils/nfsdcld/Makefile.am @@ -0,0 +1,14 @@ +## Process this file with automake to produce Makefile.in + +#man8_MANS = nfsdcld.man +#EXTRA_DIST = $(man8_MANS) + +AM_CFLAGS += -D_LARGEFILE64_SOURCE +sbin_PROGRAMS = nfsdcld + +nfsdcld_SOURCES = nfsdcld.c + +nfsdcld_LDADD = ../../support/nfs/libnfs.a $(LIBEVENT) + +MAINTAINERCLEANFILES = Makefile.in + diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c new file mode 100644 index 0000000..9fab2c3 --- /dev/null +++ b/utils/nfsdcld/nfsdcld.c @@ -0,0 +1,274 @@ +/* + * nfsdcld.c -- NFSv4 client name tracking daemon + * + * Copyright (C) 2011 Red Hat, Jeff Layton + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xlog.h" +#include "nfslib.h" +#include "cld.h" + +#ifndef PIPEFS_DIR +#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs" +#endif + +#define DEFAULT_CLD_PATH PIPEFS_DIR "/nfsd/cld" + +#define UPCALL_VERSION 1 + +/* private data structures */ +struct cld_client { + int cl_fd; + struct event cl_event; + struct cld_msg cl_msg; +}; + +/* global variables */ +static char *pipepath = DEFAULT_CLD_PATH; + +static struct option longopts[] = +{ + { "help", 0, NULL, 'h' }, + { "foreground", 0, NULL, 'F' }, + { "debug", 0, NULL, 'd' }, + { "pipe", 1, NULL, 'p' }, + { NULL, 0, 0, 0 }, +}; + +/* forward declarations */ +static void cldcb(int UNUSED(fd), short which, void *data); + +static void +usage(char *progname) +{ + printf("Usage: %s [ -hFd ] [ -p pipe ]\n", progname); +} + +static int +cld_pipe_open(struct cld_client *clnt) +{ + int fd; + + xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath); + fd = open(pipepath, O_RDWR, 0); + if (fd < 0) { + xlog(L_ERROR, "%s: open of %s failed: %m", __func__, pipepath); + return -errno; + } + + if (clnt->cl_event.ev_flags & EVLIST_INIT) + event_del(&clnt->cl_event); + if (clnt->cl_fd >= 0) + close(clnt->cl_fd); + + clnt->cl_fd = fd; + event_set(&clnt->cl_event, clnt->cl_fd, EV_READ, cldcb, clnt); + /* event_add is done by the caller */ + return 0; +} + +static int +cld_pipe_init(struct cld_client *clnt) +{ + int ret; + + clnt->cl_fd = -1; + ret = cld_pipe_open(clnt); + if (ret) + return ret; + + event_add(&clnt->cl_event, NULL); + return 0; +} + +static void +cld_not_implemented(struct cld_client *clnt) +{ + int ret; + ssize_t bsize, wsize; + struct cld_msg *cmsg = &clnt->cl_msg; + + xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__); + + /* set up reply */ + cmsg->cm_status = -EOPNOTSUPP; + + bsize = sizeof(*cmsg); + + wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); + if (wsize != bsize) + xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", + __func__, wsize); + + /* reopen pipe, just to be sure */ + ret = cld_pipe_open(clnt); + if (ret) { + xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret); + exit(ret); + } +} + +static void +cld_create(struct cld_client *clnt) +{ + int ret; + ssize_t bsize, wsize; + struct cld_msg *cmsg = &clnt->cl_msg; + + xlog(D_GENERAL, "%s: create client record", __func__); + + /* FIXME: create client record on storage here */ + + /* set up reply */ + cmsg->cm_status = 0; + + bsize = sizeof(*cmsg); + + wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); + if (wsize != bsize) { + xlog(L_ERROR, "%s: problem writing to cld pipe (%ld): %m", + __func__, wsize); + ret = cld_pipe_open(clnt); + if (ret) { + xlog(L_FATAL, "%s: unable to reopen pipe: %d", + __func__, ret); + exit(ret); + } + } +} + +static void +cldcb(int UNUSED(fd), short which, void *data) +{ + ssize_t len; + struct cld_client *clnt = data; + struct cld_msg *cmsg = &clnt->cl_msg; + + if (which != EV_READ) + goto out; + + len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg)); + if (len <= 0) { + xlog(L_ERROR, "%s: pipe read failed: %m", __func__); + cld_pipe_open(clnt); + goto out; + } + + if (cmsg->cm_vers != UPCALL_VERSION) { + xlog(L_ERROR, "%s: unsupported upcall version: %hu", + cmsg->cm_vers); + cld_pipe_open(clnt); + goto out; + } + + switch(cmsg->cm_cmd) { + case Cld_Create: + cld_create(clnt); + break; + default: + xlog(L_WARNING, "%s: command %u is not yet implemented", + __func__, cmsg->cm_cmd); + cld_not_implemented(clnt); + } +out: + event_add(&clnt->cl_event, NULL); +} + +int +main(int argc, char **argv) +{ + char arg; + int rc = 0; + bool foreground = false; + char *progname; + struct cld_client clnt; + + memset(&clnt, 0, sizeof(clnt)); + + progname = strdup(basename(argv[0])); + if (!progname) { + fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]); + return 1; + } + + event_init(); + xlog_syslog(0); + xlog_stderr(1); + + /* process command-line options */ + while ((arg = getopt_long(argc, argv, "hdFp:", longopts, + NULL)) != EOF) { + switch (arg) { + case 'd': + xlog_config(D_ALL, 1); + break; + case 'F': + foreground = true; + break; + case 'p': + pipepath = optarg; + break; + default: + usage(progname); + return 0; + } + } + + + xlog_open(progname); + if (!foreground) { + xlog_syslog(1); + xlog_stderr(0); + rc = daemon(0, 0); + if (rc) { + xlog(L_ERROR, "Unable to daemonize: %m"); + goto out; + } + } + + /* set up storage db */ + + /* set up event handler */ + rc = cld_pipe_init(&clnt); + if (rc) + goto out; + + xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__); + rc = event_dispatch(); + if (rc < 0) + xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__); + + close(clnt.cl_fd); +out: + free(progname); + return rc; +} -- 2.39.5