nfs-utils: Remove all uses of AI_ADDRCONFIG
[nfs-utils.git] / tests / nsm_client / nsm_client.c
1 /*
2  * nsm_client.c -- synthetic client and lockd simulator for testing statd
3  *
4  * Copyright (C) 2010  Red Hat, Jeff Layton <jlayton@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Very loosely based on "simulator.c" in the statd directory. Original
22  * copyright for that program follows:
23  *
24  * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <errno.h>
32 #include <getopt.h>
33 #include <netdb.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <rpc/rpc.h>
37 #include <rpc/pmap_clnt.h>
38 #include <rpcmisc.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42
43 #include "nfslib.h"
44 #include "nfsrpc.h"
45 #include "nsm.h"
46 #include "sm_inter.h"
47 #include "nlm_sm_inter.h"
48 #include "sockaddr.h"
49 #include "xcommon.h"
50
51 static void daemon_simulator(void);
52 static void sim_killer(int sig);
53 static int nsm_client_crash(char *);
54 static int nsm_client_mon(char *, char *, char *, char *, int, int);
55 static int nsm_client_stat(char *, char *);
56 static int nsm_client_notify(char *, char *, char *);
57 static int nsm_client_unmon(char *, char *, char *, int, int);
58 static int nsm_client_unmon_all(char *, char *, int, int);
59
60 extern void nlm_sm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp);
61 extern void svc_exit(void);
62
63 /*
64  * default to 15 retransmit interval, which seems to be the default for
65  * UDP clients w/ legacy glibc RPC
66  */
67 static struct timeval retrans_interval =
68 {
69         .tv_sec = 15,
70 };
71
72 static struct option longopts[] =
73 {
74         { "help", 0, 0, 'h' },
75         { "host", 0, 0, 'H' },
76         { "name", 1, 0, 'n' },
77         { "program", 1, 0, 'P' },
78         { "version", 1, 0, 'v' },
79         { NULL, 0, 0, 0 },
80 };
81
82 static int
83 usage(char *program)
84 {
85         printf("Usage:\n");
86         printf("%s [options] <command> [arg]...\n", program);
87         printf("where command is one of these with the specified args:\n");
88         printf("crash\t\t\t\ttell host to simulate crash\n");
89         printf("daemon\t\t\t\t\tstart up lockd daemon simulator\n");
90         printf("notify <mon_name> <state>\tsend a reboot notification to host\n");
91         printf("stat <mon_name>\t\t\tget status of <mon_name> on host\n");
92         printf("unmon_all\t\t\ttell host to unmon everything\n");
93         printf("unmon <mon_name>\t\t\ttell host to unmon <mon_name>\n");
94         printf("mon <mon_name> <cookie>\t\ttell host to monitor <mon_name> with private <cookie>\n");
95         return 1;
96 }
97
98 static int
99 hex2bin(char *dst, size_t dstlen, char *src)
100 {
101         int i;
102         unsigned int tmp;
103
104         for (i = 0; *src && i < dstlen; i++) {
105                 if (sscanf(src, "%2x", &tmp) != 1)
106                         return 0;
107                 dst[i] = tmp;
108                 src++;
109                 if (!*src)
110                         break;
111                 src++;
112         }
113
114         return 1;
115 }
116
117 static void
118 bin2hex(char *dst, char *src, size_t srclen)
119 {
120         int i;
121
122         for (i = 0; i < srclen; i++)
123                 dst += sprintf(dst, "%02x", 0xff & src[i]);
124 }
125
126 int
127 main(int argc, char **argv)
128 {
129         int arg, err = 0;
130         int remaining_args;
131         char my_name[NI_MAXHOST], host[NI_MAXHOST];
132         char cookie[SM_PRIV_SIZE];
133         int my_prog = NLM_SM_PROG;
134         int my_vers = NLM_SM_VERS4;
135
136         my_name[0] = '\0';
137         host[0] = '\0';
138
139         while ((arg = getopt_long(argc, argv, "hHn:P:v:", longopts,
140                                   NULL)) != EOF) {
141                 switch (arg) {
142                 case 'H':
143                         strncpy(host, optarg, sizeof(host));
144                 case 'n':
145                         strncpy(my_name, optarg, sizeof(my_name));
146                 case 'P':
147                         my_prog = atoi(optarg);
148                 case 'v':
149                         my_vers = atoi(optarg);
150                 }
151         }
152
153         remaining_args = argc - optind;
154         if (remaining_args <= 0)
155                 usage(argv[0]);
156
157         if (!my_name[0])
158                 gethostname(my_name, sizeof(my_name));
159         if (!host[0])
160                 strncpy(host, "127.0.0.1", sizeof(host));
161
162         if (!strcasecmp(argv[optind], "daemon")) {
163                 daemon_simulator();
164         } else if (!strcasecmp(argv[optind], "crash")) {
165                 err = nsm_client_crash(host);
166         } else if (!strcasecmp(argv[optind], "stat")) {
167                 if (remaining_args < 2)
168                         usage(argv[0]);
169                 err = nsm_client_stat(host, argv[optind + 2]);
170         } else if (!strcasecmp(argv[optind], "unmon_all")) {
171                 err = nsm_client_unmon_all(host, my_name, my_prog, my_vers);
172         } else if (!strcasecmp(argv[optind], "unmon")) {
173                 if (remaining_args < 2)
174                         usage(argv[0]);
175                 err = nsm_client_unmon(host, argv[optind + 1], my_name, my_prog,
176                                         my_vers);
177         } else if (!strcasecmp(argv[optind], "notify")) {
178                 if (remaining_args < 2)
179                         usage(argv[0]);
180                 err = nsm_client_notify(host, argv[optind + 1],
181                                         argv[optind + 2]);
182         } else if (!strcasecmp(argv[optind], "mon")) {
183                 if (remaining_args < 2)
184                         usage(argv[0]);
185
186                 memset(cookie, '\0', SM_PRIV_SIZE);
187                 if (!hex2bin(cookie, sizeof(cookie), argv[optind + 2])) {
188                         fprintf(stderr, "SYS:%d\n", EINVAL);
189                         printf("Unable to convert hex cookie %s to binary.\n",
190                                 argv[optind + 2]);
191                         return 1;
192                 }
193
194                 err = nsm_client_mon(host, argv[optind + 1], cookie, my_name,
195                                         my_prog, my_vers);
196         } else {
197                 err = usage(argv[0]);
198         }
199
200         return err;
201 }
202
203 static CLIENT *
204 nsm_client_get_rpcclient(const char *node)
205 {
206         unsigned short          port;
207         struct addrinfo         *ai;
208         struct addrinfo         hints = { };
209         int                     err;
210         CLIENT                  *client = NULL;
211
212 #ifndef IPV6_ENABLED
213         hints.ai_family = AF_INET;
214 #endif /* IPV6_ENABLED */
215
216         /* FIXME: allow support for providing port? */
217         err = getaddrinfo(node, NULL, &hints, &ai);
218         if (err) {
219                 fprintf(stderr, "EAI:%d\n", err);
220                 if (err == EAI_SYSTEM)
221                         fprintf(stderr, "SYS:%d\n", errno);
222                 printf("Unable to translate host to address: %s\n",
223                         err == EAI_SYSTEM ? strerror(errno) :
224                         gai_strerror(err));
225                 return client;
226         }
227
228         /* FIXME: allow for TCP too? */
229         port = nfs_getport(ai->ai_addr, ai->ai_addrlen, SM_PROG,
230                            SM_VERS, IPPROTO_UDP);
231         if (!port) {
232                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
233                 printf("Unable to determine port for service\n");
234                 goto out;
235         }
236
237         nfs_set_port(ai->ai_addr, port);
238
239         client = nfs_get_rpcclient(ai->ai_addr, ai->ai_addrlen, IPPROTO_UDP,
240                                    SM_PROG, SM_VERS, &retrans_interval);
241         if (!client) {
242                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
243                 printf("RPC client creation failed\n");
244         }
245 out:
246         freeaddrinfo(ai);
247         return client;
248 }
249
250 static int
251 nsm_client_mon(char *calling, char *monitoring, char *cookie, char *my_name,
252                 int my_prog, int my_vers)
253 {
254         CLIENT *client;
255         sm_stat_res *result;
256         mon mon;
257         int err = 0;
258
259         printf("Calling %s (as %s) to monitor %s\n", calling, my_name,
260                 monitoring);
261
262         if ((client = nsm_client_get_rpcclient(calling)) == NULL)
263                 return 1;
264
265         memcpy(mon.priv, cookie, SM_PRIV_SIZE);
266         mon.mon_id.my_id.my_name = my_name;
267         mon.mon_id.my_id.my_prog = my_prog;
268         mon.mon_id.my_id.my_vers = my_vers;
269         mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
270         mon.mon_id.mon_name = monitoring;
271
272         if (!(result = sm_mon_1(&mon, client))) {
273                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
274                 printf("%s\n", clnt_sperror(client, "sm_mon_1"));
275                 err = 1;
276                 goto mon_out;
277         }
278
279         printf("SM_MON request %s, state: %d\n",
280                 result->res_stat == stat_succ ? "successful" : "failed",
281                 result->state);
282
283         if (result->res_stat != stat_succ) {
284                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
285                 err = 1;
286         }
287
288 mon_out:
289         clnt_destroy(client);
290         return err;
291 }
292
293 static int
294 nsm_client_unmon(char *calling, char *unmonitoring, char *my_name, int my_prog,
295                 int my_vers)
296 {
297         CLIENT *client;
298         sm_stat *result;
299         mon_id mon_id;
300         int err = 0;
301
302         printf("Calling %s (as %s) to unmonitor %s\n", calling, my_name,
303                 unmonitoring);
304
305         if ((client = nsm_client_get_rpcclient(calling)) == NULL)
306                 return 1;
307
308         mon_id.my_id.my_name = my_name;
309         mon_id.my_id.my_prog = my_prog;
310         mon_id.my_id.my_vers = my_vers;
311         mon_id.my_id.my_proc = NLM_SM_NOTIFY;
312         mon_id.mon_name = unmonitoring;
313
314         if (!(result = sm_unmon_1(&mon_id, client))) {
315                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
316                 printf("%s\n", clnt_sperror(client, "sm_unmon_1"));
317                 err = 1;
318                 goto unmon_out;
319         }
320
321         printf("SM_UNMON state: %d\n", result->state);
322
323 unmon_out:
324         clnt_destroy(client);
325         return err;
326 }
327
328 static int
329 nsm_client_unmon_all(char *calling, char *my_name, int my_prog, int my_vers)
330 {
331         CLIENT *client;
332         sm_stat *result;
333         my_id my_id;
334         int err = 0;
335
336         printf("Calling %s (as %s) to unmonitor all hosts\n", calling, my_name);
337
338         if ((client = nsm_client_get_rpcclient(calling)) == NULL) {
339                 printf("RPC client creation failed\n");
340                 return 1;
341         }
342
343         my_id.my_name = my_name;
344         my_id.my_prog = my_prog;
345         my_id.my_vers = my_vers;
346         my_id.my_proc = NLM_SM_NOTIFY;
347
348         if (!(result = sm_unmon_all_1(&my_id, client))) {
349                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
350                 printf("%s\n", clnt_sperror(client, "sm_unmon_all_1"));
351                 err = 1;
352                 goto unmon_all_out;
353         }
354
355         printf("SM_UNMON_ALL state: %d\n", result->state);
356
357 unmon_all_out:
358         return err;
359 }
360
361 static int
362 nsm_client_crash(char *host)
363 {
364         CLIENT *client;
365
366         if ((client = nsm_client_get_rpcclient(host)) == NULL)
367                 return 1;
368
369         if (!sm_simu_crash_1(NULL, client)) {
370                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
371                 printf("%s\n", clnt_sperror(client, "sm_simu_crash_1"));
372                 return 1;
373         }
374
375         return 0;
376 }
377
378 static int
379 nsm_client_stat(char *calling, char *monitoring)
380 {
381         CLIENT *client;
382         sm_name checking;
383         sm_stat_res *result;
384
385         if ((client = nsm_client_get_rpcclient(calling)) == NULL)
386                 return 1;
387
388         checking.mon_name = monitoring;
389
390         if (!(result = sm_stat_1(&checking, client))) {
391                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
392                 printf("%s\n", clnt_sperror(client, "sm_stat_1"));
393                 return 1;
394         }
395
396         if (result->res_stat != stat_succ) {
397                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
398                 printf("stat_fail from %s for %s, state: %d\n", calling,
399                         monitoring, result->state);
400                 return 1;
401         }
402
403         printf("stat_succ from %s for %s, state: %d\n", calling,
404                 monitoring, result->state);
405
406         return 0;
407 }
408
409 static int
410 nsm_client_notify(char *calling, char *mon_name, char *statestr)
411 {
412         CLIENT *client;
413
414         stat_chge stat_chge = { .mon_name       = mon_name };
415
416         stat_chge.state = atoi(statestr);
417
418         if ((client = nsm_client_get_rpcclient(calling)) == NULL)
419                 return 1;
420
421         if (!sm_notify_1(&stat_chge, client)) {
422                 fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
423                 printf("%s\n", clnt_sperror(client, "sm_notify_1"));
424                 return 1;
425         }
426
427         return 0;
428 }
429
430 static void sim_killer(int sig)
431 {
432 #ifdef HAVE_LIBTIRPC
433         (void) rpcb_unset(NLM_SM_PROG, NLM_SM_VERS4, NULL);
434 #else
435         (void) pmap_unset(NLM_SM_PROG, NLM_SM_VERS4);
436 #endif
437         exit(0);
438 }
439
440 static void daemon_simulator(void)
441 {
442         signal(SIGHUP, sim_killer);
443         signal(SIGINT, sim_killer);
444         signal(SIGTERM, sim_killer);
445         /* FIXME: allow for different versions? */
446         nfs_svc_create("nlmsim", NLM_SM_PROG, NLM_SM_VERS4, nlm_sm_prog_4, 0);
447         svc_run();
448 }
449
450 void *nlm_sm_notify_4_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp)
451 {
452         static char *result;
453         char        priv[SM_PRIV_SIZE * 2 + 1];
454
455         bin2hex(priv, argp->priv, SM_PRIV_SIZE);
456
457         printf("state=%d:mon_name=%s:private=%s\n", argp->state,
458                 argp->mon_name, priv);
459         return (void *) &result;
460 }
461
462 void *nlm_sm_notify_3_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp)
463 {
464         return nlm_sm_notify_4_svc(argp, rqstp);
465 }