/* * nsm_client.c -- synthetic client and lockd simulator for testing statd * * Copyright (C) 2010 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. * * Very loosely based on "simulator.c" in the statd directory. Original * copyright for that program follows: * * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "nfslib.h" #include "nfsrpc.h" #include "nsm.h" #include "sm_inter.h" #include "nlm_sm_inter.h" #include "sockaddr.h" #include "xcommon.h" static void daemon_simulator(void); static void sim_killer(int sig); static int nsm_client_crash(char *); static int nsm_client_mon(char *, char *, char *, char *, int, int); static int nsm_client_stat(char *, char *); static int nsm_client_notify(char *, char *, char *); static int nsm_client_unmon(char *, char *, char *, int, int); static int nsm_client_unmon_all(char *, char *, int, int); extern void nlm_sm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp); extern void svc_exit(void); /* * default to 15 retransmit interval, which seems to be the default for * UDP clients w/ legacy glibc RPC */ static struct timeval retrans_interval = { .tv_sec = 15, }; static struct option longopts[] = { { "help", 0, 0, 'h' }, { "host", 0, 0, 'H' }, { "name", 1, 0, 'n' }, { "program", 1, 0, 'P' }, { "version", 1, 0, 'v' }, { NULL, 0, 0, 0 }, }; static int usage(char *program) { printf("Usage:\n"); printf("%s [options] [arg]...\n", program); printf("where command is one of these with the specified args:\n"); printf("crash\t\t\t\ttell host to simulate crash\n"); printf("daemon\t\t\t\t\tstart up lockd daemon simulator\n"); printf("notify \tsend a reboot notification to host\n"); printf("stat \t\t\tget status of on host\n"); printf("unmon_all\t\t\ttell host to unmon everything\n"); printf("unmon \t\t\ttell host to unmon \n"); printf("mon \t\ttell host to monitor with private \n"); return 1; } static int hex2bin(char *dst, size_t dstlen, char *src) { int i; unsigned int tmp; for (i = 0; *src && i < dstlen; i++) { if (sscanf(src, "%2x", &tmp) != 1) return 0; dst[i] = tmp; src++; if (!*src) break; src++; } return 1; } static void bin2hex(char *dst, char *src, size_t srclen) { int i; for (i = 0; i < srclen; i++) dst += sprintf(dst, "%02x", 0xff & src[i]); } int main(int argc, char **argv) { int arg, err = 0; int remaining_args; char my_name[NI_MAXHOST], host[NI_MAXHOST]; char cookie[SM_PRIV_SIZE]; int my_prog = NLM_SM_PROG; int my_vers = NLM_SM_VERS4; my_name[0] = '\0'; host[0] = '\0'; while ((arg = getopt_long(argc, argv, "hHn:P:v:", longopts, NULL)) != EOF) { switch (arg) { case 'H': strncpy(host, optarg, sizeof(host)); case 'n': strncpy(my_name, optarg, sizeof(my_name)); case 'P': my_prog = atoi(optarg); case 'v': my_vers = atoi(optarg); } } remaining_args = argc - optind; if (remaining_args <= 0) usage(argv[0]); if (!my_name[0]) gethostname(my_name, sizeof(my_name)); if (!host[0]) strncpy(host, "127.0.0.1", sizeof(host)); if (!strcasecmp(argv[optind], "daemon")) { daemon_simulator(); } else if (!strcasecmp(argv[optind], "crash")) { err = nsm_client_crash(host); } else if (!strcasecmp(argv[optind], "stat")) { if (remaining_args < 2) usage(argv[0]); err = nsm_client_stat(host, argv[optind + 2]); } else if (!strcasecmp(argv[optind], "unmon_all")) { err = nsm_client_unmon_all(host, my_name, my_prog, my_vers); } else if (!strcasecmp(argv[optind], "unmon")) { if (remaining_args < 2) usage(argv[0]); err = nsm_client_unmon(host, argv[optind + 1], my_name, my_prog, my_vers); } else if (!strcasecmp(argv[optind], "notify")) { if (remaining_args < 2) usage(argv[0]); err = nsm_client_notify(host, argv[optind + 1], argv[optind + 2]); } else if (!strcasecmp(argv[optind], "mon")) { if (remaining_args < 2) usage(argv[0]); memset(cookie, '\0', SM_PRIV_SIZE); if (!hex2bin(cookie, sizeof(cookie), argv[optind + 2])) { fprintf(stderr, "SYS:%d\n", EINVAL); printf("Unable to convert hex cookie %s to binary.\n", argv[optind + 2]); return 1; } err = nsm_client_mon(host, argv[optind + 1], cookie, my_name, my_prog, my_vers); } else { err = usage(argv[0]); } return err; } static CLIENT * nsm_client_get_rpcclient(const char *node) { unsigned short port; struct addrinfo *ai; struct addrinfo hints = { }; int err; CLIENT *client = NULL; #ifndef IPV6_ENABLED hints.ai_family = AF_INET; #endif /* IPV6_ENABLED */ /* FIXME: allow support for providing port? */ err = getaddrinfo(node, NULL, &hints, &ai); if (err) { fprintf(stderr, "EAI:%d\n", err); if (err == EAI_SYSTEM) fprintf(stderr, "SYS:%d\n", errno); printf("Unable to translate host to address: %s\n", err == EAI_SYSTEM ? strerror(errno) : gai_strerror(err)); return client; } /* FIXME: allow for TCP too? */ port = nfs_getport(ai->ai_addr, ai->ai_addrlen, SM_PROG, SM_VERS, IPPROTO_UDP); if (!port) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("Unable to determine port for service\n"); goto out; } nfs_set_port(ai->ai_addr, port); client = nfs_get_rpcclient(ai->ai_addr, ai->ai_addrlen, IPPROTO_UDP, SM_PROG, SM_VERS, &retrans_interval); if (!client) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("RPC client creation failed\n"); } out: nfs_freeaddrinfo(ai); return client; } static int nsm_client_mon(char *calling, char *monitoring, char *cookie, char *my_name, int my_prog, int my_vers) { CLIENT *client; sm_stat_res *result; mon mon; int err = 0; printf("Calling %s (as %s) to monitor %s\n", calling, my_name, monitoring); if ((client = nsm_client_get_rpcclient(calling)) == NULL) return 1; memcpy(mon.priv, cookie, SM_PRIV_SIZE); mon.mon_id.my_id.my_name = my_name; mon.mon_id.my_id.my_prog = my_prog; mon.mon_id.my_id.my_vers = my_vers; mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; mon.mon_id.mon_name = monitoring; if (!(result = sm_mon_1(&mon, client))) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_mon_1")); err = 1; goto mon_out; } printf("SM_MON request %s, state: %d\n", result->res_stat == stat_succ ? "successful" : "failed", result->state); if (result->res_stat != stat_succ) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); err = 1; } mon_out: clnt_destroy(client); return err; } static int nsm_client_unmon(char *calling, char *unmonitoring, char *my_name, int my_prog, int my_vers) { CLIENT *client; sm_stat *result; mon_id mon_id; int err = 0; printf("Calling %s (as %s) to unmonitor %s\n", calling, my_name, unmonitoring); if ((client = nsm_client_get_rpcclient(calling)) == NULL) return 1; mon_id.my_id.my_name = my_name; mon_id.my_id.my_prog = my_prog; mon_id.my_id.my_vers = my_vers; mon_id.my_id.my_proc = NLM_SM_NOTIFY; mon_id.mon_name = unmonitoring; if (!(result = sm_unmon_1(&mon_id, client))) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_unmon_1")); err = 1; goto unmon_out; } printf("SM_UNMON state: %d\n", result->state); unmon_out: clnt_destroy(client); return err; } static int nsm_client_unmon_all(char *calling, char *my_name, int my_prog, int my_vers) { CLIENT *client; sm_stat *result; my_id my_id; int err = 0; printf("Calling %s (as %s) to unmonitor all hosts\n", calling, my_name); if ((client = nsm_client_get_rpcclient(calling)) == NULL) { printf("RPC client creation failed\n"); return 1; } my_id.my_name = my_name; my_id.my_prog = my_prog; my_id.my_vers = my_vers; my_id.my_proc = NLM_SM_NOTIFY; if (!(result = sm_unmon_all_1(&my_id, client))) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_unmon_all_1")); err = 1; goto unmon_all_out; } printf("SM_UNMON_ALL state: %d\n", result->state); unmon_all_out: return err; } static int nsm_client_crash(char *host) { CLIENT *client; if ((client = nsm_client_get_rpcclient(host)) == NULL) return 1; if (!sm_simu_crash_1(NULL, client)) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_simu_crash_1")); return 1; } return 0; } static int nsm_client_stat(char *calling, char *monitoring) { CLIENT *client; sm_name checking; sm_stat_res *result; if ((client = nsm_client_get_rpcclient(calling)) == NULL) return 1; checking.mon_name = monitoring; if (!(result = sm_stat_1(&checking, client))) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_stat_1")); return 1; } if (result->res_stat != stat_succ) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("stat_fail from %s for %s, state: %d\n", calling, monitoring, result->state); return 1; } printf("stat_succ from %s for %s, state: %d\n", calling, monitoring, result->state); return 0; } static int nsm_client_notify(char *calling, char *mon_name, char *statestr) { CLIENT *client; stat_chge stat_chge = { .mon_name = mon_name }; stat_chge.state = atoi(statestr); if ((client = nsm_client_get_rpcclient(calling)) == NULL) return 1; if (!sm_notify_1(&stat_chge, client)) { fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); printf("%s\n", clnt_sperror(client, "sm_notify_1")); return 1; } return 0; } static void sim_killer(int sig) { #ifdef HAVE_LIBTIRPC (void) rpcb_unset(NLM_SM_PROG, NLM_SM_VERS4, NULL); #else (void) pmap_unset(NLM_SM_PROG, NLM_SM_VERS4); #endif exit(0); } static void daemon_simulator(void) { signal(SIGHUP, sim_killer); signal(SIGINT, sim_killer); signal(SIGTERM, sim_killer); /* FIXME: allow for different versions? */ nfs_svc_create("nlmsim", NLM_SM_PROG, NLM_SM_VERS4, nlm_sm_prog_4, 0); svc_run(); } void *nlm_sm_notify_4_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) { static char *result; char priv[SM_PRIV_SIZE * 2 + 1]; bin2hex(priv, argp->priv, SM_PRIV_SIZE); printf("state=%d:mon_name=%s:private=%s\n", argp->state, argp->mon_name, priv); return (void *) &result; } void *nlm_sm_notify_3_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) { return nlm_sm_notify_4_svc(argp, rqstp); }