xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_config.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * BSD 3 Clause License
7  *
8  * Copyright (c) 2007, The Storage Networking Industry Association.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 	- Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  *
16  * 	- Redistributions in binary form must reproduce the above copyright
17  *	  notice, this list of conditions and the following disclaimer in
18  *	  the documentation and/or other materials provided with the
19  *	  distribution.
20  *
21  *	- Neither the name of The Storage Networking Industry Association (SNIA)
22  *	  nor the names of its contributors may be used to endorse or promote
23  *	  products derived from this software without specific prior written
24  *	  permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /* Copyright (c) 2007, The Storage Networking Industry Association. */
39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
40 /* Copyright 2014 Nexenta Systems, Inc.  All rights reserved. */
41 
42 #include <dirent.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/stat.h>
48 #include <sys/mnttab.h>
49 #include <sys/mntent.h>
50 #include <sys/mntio.h>
51 #include <sys/statvfs.h>
52 #include <sys/utsname.h>
53 #include <sys/scsi/scsi.h>
54 #include <unistd.h>
55 #include <sys/systeminfo.h>
56 #include "ndmpd_common.h"
57 #include "ndmpd.h"
58 
59 static void simple_get_attrs(ulong_t *attributes);
60 
61 /*
62  * Number of environment variable for the file system
63  * info in V3 net_fs_info.
64  */
65 #define	V3_N_FS_ENVS	4
66 
67 /*
68  * Is the file system a valid one to be reported to the
69  * clients?
70  */
71 #define	IS_VALID_FS(fs) (fs && ( \
72 	strcasecmp(fs->mnt_fstype, MNTTYPE_UFS) == 0 || \
73 	strcasecmp(fs->mnt_fstype, MNTTYPE_ZFS) == 0 || \
74 	strcasecmp(fs->mnt_fstype, MNTTYPE_NFS) == 0 || \
75 	strcasecmp(fs->mnt_fstype, MNTTYPE_NFS3) == 0 || \
76 	strcasecmp(fs->mnt_fstype, MNTTYPE_NFS4) == 0))
77 
78 #define	MNTTYPE_LEN	10
79 
80 extern struct fs_ops sfs2_ops;
81 extern struct fs_ops sfs2cpv_ops;
82 
83 
84 /*
85  * ************************************************************************
86  * NDMP V2 HANDLERS
87  * ************************************************************************
88  */
89 
90 /*
91  * ndmpd_config_get_host_info_v2
92  *
93  * This handler handles the ndmp_config_get_host_info request.
94  * Host specific information is returned.
95  *
96  * Parameters:
97  *   connection (input) - connection handle.
98  *   body       (input) - request message body.
99  *
100  * Returns:
101  *   void
102  */
103 /*ARGSUSED*/
104 void
105 ndmpd_config_get_host_info_v2(ndmp_connection_t *connection, void *body)
106 {
107 	ndmp_config_get_host_info_reply_v2 reply;
108 	ndmp_auth_type auth_types[2];
109 	char buf[HOSTNAMELEN + 1];
110 	struct utsname uts;
111 	char hostidstr[16];
112 	ulong_t hostid;
113 
114 	(void) memset((void*)&reply, 0, sizeof (reply));
115 	(void) memset(buf, 0, sizeof (buf));
116 	(void) gethostname(buf, sizeof (buf));
117 
118 	reply.error = NDMP_NO_ERR;
119 	reply.hostname = buf;
120 	(void) uname(&uts);
121 	reply.os_type = uts.sysname;
122 	reply.os_vers = uts.release;
123 
124 	if (sysinfo(SI_HW_SERIAL, hostidstr, sizeof (hostidstr)) < 0) {
125 		NDMP_LOG(LOG_DEBUG, "sysinfo error: %m.");
126 		reply.error = NDMP_UNDEFINED_ERR;
127 	}
128 
129 	/*
130 	 * Convert the hostid to hex. The returned string must match
131 	 * the string returned by hostid(1).
132 	 */
133 	hostid = strtoul(hostidstr, 0, 0);
134 	(void) snprintf(hostidstr, sizeof (hostidstr), "%lx", hostid);
135 	reply.hostid = hostidstr;
136 
137 	auth_types[0] = NDMP_AUTH_TEXT;
138 	reply.auth_type.auth_type_len = 1;
139 	reply.auth_type.auth_type_val = auth_types;
140 
141 	ndmp_send_reply(connection, (void *) &reply,
142 	    "sending ndmp_config_get_host_info reply");
143 }
144 
145 
146 /*
147  * ndmpd_config_get_butype_attr_v2
148  *
149  * This handler handles the ndmp_config_get_butype_attr request.
150  * Information about the specified backup type is returned.
151  *
152  * Parameters:
153  *   connection (input) - connection handle.
154  *   body       (input) - request message body.
155  *
156  * Returns:
157  *   void
158  */
159 void
160 ndmpd_config_get_butype_attr_v2(ndmp_connection_t *connection, void *body)
161 {
162 	ndmp_config_get_butype_attr_request *request;
163 	ndmp_config_get_butype_attr_reply reply;
164 
165 	request = (ndmp_config_get_butype_attr_request *)body;
166 
167 	reply.error = NDMP_NO_ERR;
168 
169 	if (strcmp(request->name, "dump") == 0) {
170 		(void) simple_get_attrs(&reply.attrs);
171 	} else if (strcmp(request->name, "tar") == 0) {
172 		reply.attrs = NDMP_NO_BACKUP_FILELIST;
173 	} else {
174 		NDMP_LOG(LOG_ERR, "Invalid backup type: %s.", request->name);
175 		NDMP_LOG(LOG_ERR,
176 		    "Supported backup types are 'dump' and 'tar' only.");
177 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
178 	}
179 
180 	ndmp_send_reply(connection, (void *) &reply,
181 	    "sending ndmp_config_get_butype_attr reply");
182 }
183 
184 
185 /*
186  * ndmpd_config_get_mover_type_v2
187  *
188  * This handler handles the ndmp_config_get_mover_type request.
189  * Information about the supported mover types is returned.
190  *
191  * Parameters:
192  *   connection (input) - connection handle.
193  *   body       (input) - request message body.
194  *
195  * Returns:
196  *   void
197  */
198 /*ARGSUSED*/
199 void
200 ndmpd_config_get_mover_type_v2(ndmp_connection_t *connection, void *body)
201 {
202 	ndmp_config_get_mover_type_reply reply;
203 	ndmp_addr_type types[2];
204 
205 	types[0] = NDMP_ADDR_LOCAL;
206 	types[1] = NDMP_ADDR_TCP;
207 
208 	reply.error = NDMP_NO_ERR;
209 	reply.methods.methods_len = 2;
210 	reply.methods.methods_val = types;
211 
212 	ndmp_send_reply(connection, (void *) &reply,
213 	    "sending ndmp_config_get_mover_type reply");
214 }
215 
216 
217 
218 /*
219  * ndmpd_config_get_auth_attr_v2
220  *
221  * This handler handles the ndmp_config_get_auth_attr request.
222  * Authorization type specific information is returned.
223  *
224  * Parameters:
225  *   connection (input) - connection handle.
226  *   body       (input) - request message body.
227  *
228  * Returns:
229  *   void
230  */
231 void
232 ndmpd_config_get_auth_attr_v2(ndmp_connection_t *connection, void *body)
233 {
234 	ndmp_config_get_auth_attr_request *request;
235 	ndmp_config_get_auth_attr_reply reply;
236 	ndmpd_session_t *session = ndmp_get_client_data(connection);
237 
238 	request = (ndmp_config_get_auth_attr_request *)body;
239 
240 	reply.error = NDMP_NO_ERR;
241 	reply.server_attr.auth_type = request->auth_type;
242 
243 	switch (request->auth_type) {
244 	case NDMP_AUTH_TEXT:
245 		break;
246 	case NDMP_AUTH_MD5:
247 		/* Create a 64 byte random session challenge */
248 		randomize(session->ns_challenge, MD5_CHALLENGE_SIZE);
249 		(void) memcpy(reply.server_attr.ndmp_auth_attr_u.challenge,
250 		    session->ns_challenge, MD5_CHALLENGE_SIZE);
251 		break;
252 	case NDMP_AUTH_NONE:
253 		/* FALL THROUGH */
254 	default:
255 		NDMP_LOG(LOG_ERR, "Invalid authentication type: %d.",
256 		    request->auth_type);
257 		NDMP_LOG(LOG_ERR,
258 		    "Supported authentication types are md5 and cleartext.");
259 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
260 		break;
261 	}
262 
263 	ndmp_send_reply(connection, (void *) &reply,
264 	    "sending ndmp_config_get_auth_attr reply");
265 }
266 
267 
268 /*
269  * ************************************************************************
270  * NDMP V3 HANDLERS
271  * ************************************************************************
272  */
273 
274 /*
275  * ndmpd_config_get_host_info_v3
276  *
277  * This handler handles the ndmp_config_get_host_info request.
278  * Host specific information is returned.
279  *
280  * Parameters:
281  *   connection (input) - connection handle.
282  *   body       (input) - request message body.
283  *
284  * Returns:
285  *   void
286  */
287 /*ARGSUSED*/
288 void
289 ndmpd_config_get_host_info_v3(ndmp_connection_t *connection, void *body)
290 {
291 	ndmp_config_get_host_info_reply_v3 reply;
292 	char buf[HOSTNAMELEN+1];
293 	struct utsname uts;
294 	char hostidstr[16];
295 	ulong_t hostid;
296 
297 	(void) memset((void*)&reply, 0, sizeof (reply));
298 	(void) memset(buf, 0, sizeof (buf));
299 	(void) gethostname(buf, sizeof (buf));
300 
301 
302 	reply.error = NDMP_NO_ERR;
303 	reply.hostname = buf;
304 	(void) uname(&uts);
305 	reply.os_type = uts.sysname;
306 	reply.os_vers = uts.release;
307 
308 	if (sysinfo(SI_HW_SERIAL, hostidstr, sizeof (hostidstr)) < 0) {
309 
310 		NDMP_LOG(LOG_DEBUG, "sysinfo error: %m.");
311 		reply.error = NDMP_UNDEFINED_ERR;
312 	}
313 
314 	/*
315 	 * Convert the hostid to hex. The returned string must match
316 	 * the string returned by hostid(1).
317 	 */
318 	hostid = strtoul(hostidstr, 0, 0);
319 	(void) snprintf(hostidstr, sizeof (hostidstr), "%lx", hostid);
320 	reply.hostid = hostidstr;
321 
322 	ndmp_send_reply(connection, (void *) &reply,
323 	    "sending ndmp_config_get_host_info reply");
324 }
325 
326 
327 /*
328  * ndmpd_config_get_connection_type_v3
329  *
330  * This handler handles the ndmp_config_get_connection_type_request.
331  * A list of supported data connection types is returned.
332  *
333  * Parameters:
334  *   connection (input) - connection handle.
335  *   body       (input) - request message body.
336  *
337  * Returns:
338  *   void
339  */
340 /*ARGSUSED*/
341 void
342 ndmpd_config_get_connection_type_v3(ndmp_connection_t *connection,
343     void *body)
344 {
345 	ndmp_config_get_connection_type_reply_v3 reply;
346 	ndmp_addr_type addr_types[2];
347 
348 	(void) memset((void*)&reply, 0, sizeof (reply));
349 
350 	reply.error = NDMP_NO_ERR;
351 
352 	addr_types[0] = NDMP_ADDR_LOCAL;
353 	addr_types[1] = NDMP_ADDR_TCP;
354 	reply.addr_types.addr_types_len = 2;
355 	reply.addr_types.addr_types_val = addr_types;
356 
357 	ndmp_send_reply(connection, (void *) &reply,
358 	    "sending config_get_connection_type_v3 reply");
359 }
360 
361 
362 
363 /*
364  * ndmpd_config_get_auth_attr_v3
365  *
366  * This handler handles the ndmp_config_get_auth_attr request.
367  * Authorization type specific information is returned.
368  *
369  * Parameters:
370  *   connection (input) - connection handle.
371  *   body       (input) - request message body.
372  *
373  * Returns:
374  *   void
375  */
376 void
377 ndmpd_config_get_auth_attr_v3(ndmp_connection_t *connection, void *body)
378 {
379 	ndmp_config_get_auth_attr_request *request;
380 	ndmp_config_get_auth_attr_reply reply;
381 	ndmpd_session_t *session = ndmp_get_client_data(connection);
382 
383 	request = (ndmp_config_get_auth_attr_request *)body;
384 
385 	(void) memset((void*)&reply, 0, sizeof (reply));
386 	reply.error = NDMP_NO_ERR;
387 	reply.server_attr.auth_type = request->auth_type;
388 
389 	switch (request->auth_type) {
390 	case NDMP_AUTH_TEXT:
391 		break;
392 	case NDMP_AUTH_MD5:
393 		/* Create a 64 bytes random session challenge */
394 		randomize(session->ns_challenge, MD5_CHALLENGE_SIZE);
395 		(void) memcpy(reply.server_attr.ndmp_auth_attr_u.challenge,
396 		    session->ns_challenge, MD5_CHALLENGE_SIZE);
397 		break;
398 	case NDMP_AUTH_NONE:
399 		/* FALL THROUGH */
400 	default:
401 		NDMP_LOG(LOG_ERR, "Invalid authentication type: %d.",
402 		    request->auth_type);
403 		NDMP_LOG(LOG_ERR,
404 		    "Supported authentication types are md5 and cleartext.");
405 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
406 		break;
407 	}
408 
409 	ndmp_send_reply(connection, (void *) &reply,
410 	    "sending ndmp_config_get_auth_attr_v3 reply");
411 }
412 
413 
414 /*
415  * ndmpd_config_get_butype_info_v3
416  *
417  * This handler handles the ndmp_config_get_butype_info_request.
418  * Information about all supported backup types are returned.
419  *
420  * Parameters:
421  *   connection (input) - connection handle.
422  *   body       (input) - request message body.
423  *
424  * Returns:
425  *   void
426  */
427 /*ARGSUSED*/
428 void
429 ndmpd_config_get_butype_info_v3(ndmp_connection_t *connection, void *body)
430 {
431 	ndmp_config_get_butype_info_reply_v3 reply;
432 	ndmp_butype_info info[3];
433 	ndmp_pval envs[8];
434 	ulong_t attrs;
435 	ndmp_pval *envp = envs;
436 
437 	ndmp_pval zfs_envs[9];
438 	ulong_t zfs_attrs;
439 	ndmp_pval *zfs_envp = zfs_envs;
440 
441 	(void) memset((void*)&reply, 0, sizeof (reply));
442 
443 	/*
444 	 * Supported environment variables and their default values
445 	 * for dump and tar.
446 	 *
447 	 * The environment variables for dump and tar format are the
448 	 * same, because we use the same backup engine for both.
449 	 */
450 	NDMP_SETENV(envp, "PREFIX", "");
451 	NDMP_SETENV(envp, "TYPE", "");
452 	NDMP_SETENV(envp, "DIRECT", "n");
453 	NDMP_SETENV(envp, "HIST", "n");
454 	NDMP_SETENV(envp, "FILESYSTEM", "");
455 	NDMP_SETENV(envp, "LEVEL", "0");
456 	NDMP_SETENV(envp, "UPDATE", "TRUE");
457 	NDMP_SETENV(envp, "BASE_DATE", "");
458 
459 	attrs = NDMP_BUTYPE_BACKUP_FILE_HISTORY |
460 	    NDMP_BUTYPE_RECOVER_FILELIST |
461 	    NDMP_BUTYPE_BACKUP_DIRECT |
462 	    NDMP_BUTYPE_BACKUP_INCREMENTAL |
463 	    NDMP_BUTYPE_BACKUP_UTF8 |
464 	    NDMP_BUTYPE_RECOVER_UTF8;
465 
466 	/* If DAR supported */
467 	if (ndmp_dar_support)
468 		attrs |= NDMP_BUTYPE_RECOVER_DIRECT;
469 
470 	/* tar backup type */
471 	info[0].butype_name = "tar";
472 	info[0].default_env.default_env_len = ARRAY_LEN(envs, ndmp_pval);
473 	info[0].default_env.default_env_val = envs;
474 	info[0].attrs = attrs;
475 
476 	/* dump backup type */
477 	info[1].butype_name = "dump";
478 	info[1].default_env.default_env_len = ARRAY_LEN(envs, ndmp_pval);
479 	info[1].default_env.default_env_val = envs;
480 	info[1].attrs = attrs;
481 
482 	/*
483 	 * Supported environment variables and their default values
484 	 * for type "zfs."
485 	 */
486 
487 	NDMP_SETENV(zfs_envp, "PREFIX", "");
488 	NDMP_SETENV(zfs_envp, "FILESYSTEM", "");
489 	NDMP_SETENV(zfs_envp, "TYPE", "zfs");
490 	NDMP_SETENV(zfs_envp, "HIST", "n");
491 	NDMP_SETENV(zfs_envp, "LEVEL", "0");
492 	NDMP_SETENV(zfs_envp, "ZFS_MODE", "recursive");
493 	NDMP_SETENV(zfs_envp, "ZFS_FORCE", "FALSE");
494 	NDMP_SETENV(zfs_envp, "UPDATE", "TRUE");
495 	NDMP_SETENV(zfs_envp, "DMP_NAME", "level");
496 
497 	zfs_attrs = NDMP_BUTYPE_BACKUP_UTF8 |
498 	    NDMP_BUTYPE_RECOVER_UTF8 |
499 	    NDMP_BUTYPE_BACKUP_DIRECT |
500 	    NDMP_BUTYPE_BACKUP_INCREMENTAL;
501 
502 	/* zfs backup type */
503 	info[2].butype_name = "zfs";
504 	info[2].default_env.default_env_len = ARRAY_LEN(zfs_envs, ndmp_pval);
505 	info[2].default_env.default_env_val = zfs_envs;
506 	info[2].attrs = zfs_attrs;
507 
508 	reply.error = NDMP_NO_ERR;
509 	reply.butype_info.butype_info_len = ARRAY_LEN(info, ndmp_butype_info);
510 	reply.butype_info.butype_info_val = info;
511 
512 	ndmp_send_reply(connection, (void *)&reply,
513 	    "sending ndmp_config_get_butype_info reply");
514 }
515 
516 /*
517  * ndmpd_config_get_fs_info_v3
518  *
519  * This handler handles the ndmp_config_get_fs_info_request.
520  * Information about all mounted file systems is returned.
521  *
522  * Parameters:
523  *   connection (input) - connection handle.
524  *   body       (input) - request message body.
525  *
526  * Returns:
527  *   void
528  */
529 /*ARGSUSED*/
530 void
531 ndmpd_config_get_fs_info_v3(ndmp_connection_t *connection, void *body)
532 {
533 	ndmp_config_get_fs_info_reply_v3 reply;
534 	ndmp_fs_info_v3 *fsip = NULL, *fsip_save = NULL; /* FS info pointer */
535 	int len = 0, nmnt, fd;
536 	int log_dev_len;
537 	FILE *fp = NULL;
538 	struct mnttab mt, *fs;
539 	struct statvfs64 stat_buf;
540 	ndmp_pval *envp, *save;
541 
542 	(void) memset((void*)&reply, 0, sizeof (reply));
543 	reply.error = NDMP_NO_ERR;
544 
545 	if ((fd = open(MNTTAB, O_RDONLY)) == -1) {
546 		NDMP_LOG(LOG_ERR, "File mnttab open error: %m.");
547 		reply.error = NDMP_UNDEFINED_ERR;
548 		goto send_reply;
549 	}
550 
551 	/* nothing was found, send an empty reply */
552 	if (ioctl(fd, MNTIOC_NMNTS, &nmnt) != 0 || nmnt <= 0) {
553 		(void) close(fd);
554 		NDMP_LOG(LOG_ERR, "No file system found.");
555 		goto send_reply;
556 	}
557 
558 	fp = fdopen(fd, "r");
559 	if (!fp) {
560 		(void) close(fd);
561 		NDMP_LOG(LOG_ERR, "File mnttab open error: %m.");
562 		reply.error = NDMP_UNDEFINED_ERR;
563 		goto send_reply;
564 	}
565 
566 	fsip_save = fsip = ndmp_malloc(sizeof (ndmp_fs_info_v3) * nmnt);
567 	if (!fsip) {
568 		(void) fclose(fp);
569 		reply.error = NDMP_NO_MEM_ERR;
570 		goto send_reply;
571 	}
572 
573 	/*
574 	 * Re-read the directory and set up file system information.
575 	 */
576 	rewind(fp);
577 	while (len < nmnt && (getmntent(fp, &mt) == 0))
578 
579 	{
580 		fs = &mt;
581 		log_dev_len = strlen(mt.mnt_mountp)+2;
582 		if (!IS_VALID_FS(fs))
583 			continue;
584 
585 		fsip->fs_logical_device = ndmp_malloc(log_dev_len);
586 		fsip->fs_type = ndmp_malloc(MNTTYPE_LEN);
587 		if (!fsip->fs_logical_device || !fsip->fs_type) {
588 			free(fsip->fs_logical_device);
589 			free(fsip->fs_type);
590 			reply.error = NDMP_NO_MEM_ERR;
591 			break;
592 		}
593 		(void) snprintf(fsip->fs_type, MNTTYPE_LEN, "%s",
594 		    fs->mnt_fstype);
595 		(void) snprintf(fsip->fs_logical_device, log_dev_len, "%s",
596 		    fs->mnt_mountp);
597 		fsip->invalid = 0;
598 
599 		if (statvfs64(fs->mnt_mountp, &stat_buf) < 0) {
600 			NDMP_LOG(LOG_DEBUG,
601 			    "statvfs(%s) error.", fs->mnt_mountp);
602 			fsip->fs_status =
603 			    "statvfs error: unable to determine filesystem"
604 			    " attributes";
605 		} else {
606 			fsip->invalid = 0;
607 			fsip->total_size =
608 			    long_long_to_quad((u_longlong_t)stat_buf.f_frsize *
609 			    (u_longlong_t)stat_buf.f_blocks);
610 			fsip->used_size =
611 			    long_long_to_quad((u_longlong_t)stat_buf.f_frsize *
612 			    (u_longlong_t)(stat_buf.f_blocks-stat_buf.f_bfree));
613 
614 			fsip->avail_size =
615 			    long_long_to_quad((u_longlong_t)stat_buf.f_frsize *
616 			    (u_longlong_t)stat_buf.f_bfree);
617 			fsip->total_inodes =
618 			    long_long_to_quad((u_longlong_t)stat_buf.f_files);
619 			fsip->used_inodes =
620 			    long_long_to_quad((u_longlong_t)(stat_buf.f_files -
621 			    stat_buf.f_ffree));
622 			fsip->fs_status = "";
623 		}
624 		save = envp = ndmp_malloc(sizeof (ndmp_pval) * V3_N_FS_ENVS);
625 		if (!envp) {
626 			free(fsip->fs_logical_device);
627 			free(fsip->fs_type);
628 			reply.error = NDMP_NO_MEM_ERR;
629 			break;
630 		}
631 		(void) memset((void*)save, 0,
632 		    V3_N_FS_ENVS * sizeof (ndmp_pval));
633 
634 		fsip->fs_env.fs_env_val = envp;
635 		NDMP_SETENV(envp, "LOCAL", "y");
636 		NDMP_SETENV(envp, "TYPE", fsip->fs_type);
637 		NDMP_SETENV(envp, "AVAILABLE_BACKUP", "tar,dump");
638 
639 		if (FS_READONLY(fs) == 0) {
640 			NDMP_SETENV(envp, "AVAILABLE_RECOVERY", "tar,dump");
641 		}
642 
643 		fsip->fs_env.fs_env_len = envp - save;
644 		len++;
645 		fsip++;
646 	}
647 	(void) fclose(fp);
648 
649 send_reply:
650 	if (reply.error == NDMP_NO_ERR) {
651 		reply.fs_info.fs_info_len = len;
652 		reply.fs_info.fs_info_val = fsip_save;
653 	}
654 	ndmp_send_reply(connection, (void *)&reply,
655 	    "error sending ndmp_config_get_fs_info reply");
656 
657 	while (fsip > fsip_save) {
658 		fsip--;
659 		free(fsip->fs_logical_device);
660 		free(fsip->fs_env.fs_env_val);
661 		free(fsip->fs_type);
662 	}
663 
664 	free(fsip);
665 }
666 
667 
668 /*
669  * ndmpd_config_get_tape_info_v3
670  *
671  * This handler handles the ndmp_config_get_tape_info_request.
672  * Information about all connected tape drives is returned.
673  *
674  * Parameters:
675  *   connection (input) - connection handle.
676  *   body       (input) - request message body.
677  *
678  * Returns:
679  *   void
680  */
681 /*ARGSUSED*/
682 void
683 ndmpd_config_get_tape_info_v3(ndmp_connection_t *connection, void *body)
684 {
685 	ndmp_config_get_tape_info_reply_v3 reply;
686 	ndmp_device_info_v3 *tip, *tip_save = NULL; /* tape info pointer */
687 	ndmp_device_capability_v3 *dcp;
688 	ndmp_device_capability_v3 *dcp_save = NULL; /* dev capability pointer */
689 	int i, n, max;
690 	sasd_drive_t *sd;
691 	scsi_link_t *sl;
692 	ndmp_pval *envp, *envp_save = NULL;
693 	ndmp_pval *envp_head;
694 
695 	(void) memset((void*)&reply, 0, sizeof (reply));
696 	max = sasd_dev_count();
697 
698 	tip_save = tip = ndmp_malloc(sizeof (ndmp_device_info_v3) * max);
699 	dcp_save = dcp = ndmp_malloc(sizeof (ndmp_device_capability_v3) * max);
700 	envp_save = envp = ndmp_malloc(sizeof (ndmp_pval) * max * 3);
701 	if (!tip_save || !dcp_save || !envp_save) {
702 		free(tip_save);
703 		free(dcp_save);
704 		free(envp_save);
705 		reply.error = NDMP_NO_MEM_ERR;
706 		ndmp_send_reply(connection, (void *)&reply,
707 		    "error sending ndmp_config_get_tape_info reply");
708 		return;
709 	}
710 
711 	reply.error = NDMP_NO_ERR;
712 
713 	for (i = n = 0; i < max; i++) {
714 		if (!(sl = sasd_dev_slink(i)) || !(sd = sasd_drive(i)))
715 			continue;
716 		if (sl->sl_type != DTYPE_SEQUENTIAL)
717 			continue;
718 		/*
719 		 * Don't report dead links.
720 		 */
721 		if ((access(sd->sd_name, F_OK) == -1) && (errno == ENOENT))
722 			continue;
723 
724 		NDMP_LOG(LOG_DEBUG,
725 		    "model \"%s\" dev \"%s\"", sd->sd_id, sd->sd_name);
726 
727 		envp_head = envp;
728 		NDMP_SETENV(envp, "EXECUTE_CDB", "b");
729 		NDMP_SETENV(envp, "SERIAL_NUMBER", sd->sd_serial);
730 		NDMP_SETENV(envp, "WORLD_WIDE_NAME", sd->sd_wwn);
731 
732 		tip->model = sd->sd_id; /* like "DLT7000	 " */
733 		tip->caplist.caplist_len = 1;
734 		tip->caplist.caplist_val = dcp;
735 		dcp->device = sd->sd_name; /* like "isp1t060" */
736 		dcp->attr = 0;
737 		dcp->capability.capability_len = 3;
738 		dcp->capability.capability_val = envp_head;
739 		tip++;
740 		dcp++;
741 		n++;
742 	}
743 
744 	NDMP_LOG(LOG_DEBUG, "n %d", n);
745 
746 	/*
747 	 * We should not receive the get_tape_info when three-way backup is
748 	 * running and we are acting as just data, but some clients try
749 	 * to get the Tape information anyway.
750 	 */
751 	if (n == 0 || max <= 0) {
752 		reply.error = NDMP_NO_DEVICE_ERR;
753 		ndmp_send_reply(connection, (void *)&reply,
754 		    "error sending ndmp_config_get_tape_info reply");
755 		free(tip_save); free(dcp_save); free(envp_save);
756 		return;
757 	}
758 
759 
760 	reply.tape_info.tape_info_len = n;
761 	reply.tape_info.tape_info_val = tip_save;
762 
763 	ndmp_send_reply(connection, (void *)&reply,
764 	    "error sending ndmp_config_get_tape_info reply");
765 
766 	free(tip_save);
767 	free(dcp_save);
768 	free(envp_save);
769 }
770 
771 
772 /*
773  * ndmpd_config_get_scsi_info_v3
774  *
775  * This handler handles the ndmp_config_get_tape_scsi_request.
776  * Information about all connected scsi tape stacker and jukeboxes
777  * is returned.
778  *
779  * Parameters:
780  *   connection (input) - connection handle.
781  *   body       (input) - request message body.
782  *
783  * Returns:
784  *   void
785  */
786 /*ARGSUSED*/
787 void
788 ndmpd_config_get_scsi_info_v3(ndmp_connection_t *connection, void *body)
789 {
790 	ndmp_config_get_scsi_info_reply_v3 reply;
791 	ndmp_device_info_v3 *sip, *sip_save;
792 	ndmp_device_capability_v3 *dcp, *dcp_save;
793 	int i, n, max;
794 	sasd_drive_t *sd;
795 	scsi_link_t *sl;
796 	ndmp_pval *envp, *envp_save = NULL;
797 	ndmp_pval *envp_head;
798 
799 	(void) memset((void*)&reply, 0, sizeof (reply));
800 	max = sasd_dev_count();
801 	sip_save = sip = ndmp_malloc(sizeof (ndmp_device_info_v3) * max);
802 	dcp_save = dcp = ndmp_malloc(sizeof (ndmp_device_capability_v3) * max);
803 	envp_save = envp = ndmp_malloc(sizeof (ndmp_pval) * max * 2);
804 	if (!sip_save || !dcp_save || !envp_save) {
805 		free(sip_save);
806 		free(dcp_save);
807 		free(envp_save);
808 		reply.error = NDMP_NO_MEM_ERR;
809 		ndmp_send_reply(connection, (void *)&reply,
810 		    "error sending ndmp_config_get_scsi_info reply");
811 		return;
812 	}
813 
814 	reply.error = NDMP_NO_ERR;
815 	for (i = n = 0; i < max; i++) {
816 		if (!(sl = sasd_dev_slink(i)) || !(sd = sasd_drive(i)))
817 			continue;
818 		if (sl->sl_type != DTYPE_CHANGER)
819 			continue;
820 		/*
821 		 * Don't report dead links.
822 		 */
823 		if ((access(sd->sd_name, F_OK) == -1) && (errno == ENOENT))
824 			continue;
825 
826 		NDMP_LOG(LOG_DEBUG,
827 		    "model \"%s\" dev \"%s\"", sd->sd_id, sd->sd_name);
828 
829 		envp_head = envp;
830 		NDMP_SETENV(envp, "SERIAL_NUMBER", sd->sd_serial);
831 		NDMP_SETENV(envp, "WORLD_WIDE_NAME", sd->sd_wwn);
832 
833 		sip->model = sd->sd_id; /* like "Powerstor L200  " */
834 		sip->caplist.caplist_len = 1;
835 		sip->caplist.caplist_val = dcp;
836 		dcp->device = sd->sd_name; /* like "isp1m000" */
837 
838 		dcp->attr = 0;
839 		dcp->capability.capability_len = 2;
840 		dcp->capability.capability_val = envp_head;
841 		sip++;
842 		dcp++;
843 		n++;
844 	}
845 
846 	NDMP_LOG(LOG_DEBUG, "n %d", n);
847 
848 	reply.scsi_info.scsi_info_len = n;
849 	reply.scsi_info.scsi_info_val = sip_save;
850 
851 	ndmp_send_reply(connection, (void *)&reply,
852 	    "error sending ndmp_config_get_scsi_info reply");
853 
854 	free(sip_save);
855 	free(dcp_save);
856 	free(envp_save);
857 }
858 
859 
860 /*
861  * ndmpd_config_get_server_info_v3
862  *
863  * This handler handles the ndmp_config_get_server_info request.
864  * Host specific information is returned.
865  *
866  * Parameters:
867  *   connection (input) - connection handle.
868  *   body       (input) - request message body.
869  *
870  * Returns:
871  *   void
872  */
873 /*ARGSUSED*/
874 void
875 ndmpd_config_get_server_info_v3(ndmp_connection_t *connection, void *body)
876 {
877 	ndmp_config_get_server_info_reply_v3 reply;
878 	ndmp_auth_type auth_types[2];
879 	char rev_number[10];
880 	ndmpd_session_t *session = ndmp_get_client_data(connection);
881 
882 	(void) memset((void*)&reply, 0, sizeof (reply));
883 	reply.error = NDMP_NO_ERR;
884 
885 	if (connection->conn_authorized ||
886 	    session->ns_protocol_version != NDMPV4) {
887 		reply.vendor_name = VENDOR_NAME;
888 		reply.product_name = PRODUCT_NAME;
889 		(void) snprintf(rev_number, sizeof (rev_number), "%d",
890 		    ndmp_ver);
891 		reply.revision_number = rev_number;
892 	} else {
893 		reply.vendor_name = "\0";
894 		reply.product_name = "\0";
895 		reply.revision_number = "\0";
896 	}
897 
898 	NDMP_LOG(LOG_DEBUG,
899 	    "vendor \"%s\", product \"%s\" rev \"%s\"",
900 	    reply.vendor_name, reply.product_name, reply.revision_number);
901 
902 	auth_types[0] = NDMP_AUTH_TEXT;
903 	auth_types[1] = NDMP_AUTH_MD5;
904 	reply.auth_type.auth_type_len = ARRAY_LEN(auth_types, ndmp_auth_type);
905 	reply.auth_type.auth_type_val = auth_types;
906 
907 	ndmp_send_reply(connection, (void *)&reply,
908 	    "error sending ndmp_config_get_server_info reply");
909 }
910 
911 
912 
913 /*
914  * ************************************************************************
915  * NDMP V4 HANDLERS
916  * ************************************************************************
917  */
918 
919 /*
920  * ndmpd_config_get_butype_info_v4
921  *
922  * This handler handles the ndmp_config_get_butype_info_request.
923  * Information about all supported backup types are returned.
924  *
925  * Parameters:
926  *   connection (input) - connection handle.
927  *   body       (input) - request message body.
928  *
929  * Returns:
930  *   void
931  */
932 /*ARGSUSED*/
933 void
934 ndmpd_config_get_butype_info_v4(ndmp_connection_t *connection, void *body)
935 {
936 	ndmp_config_get_butype_info_reply_v4 reply;
937 	ndmp_butype_info info[3];
938 
939 	ndmp_pval envs[12];
940 	ulong_t attrs;
941 	ndmp_pval *envp = envs;
942 
943 	ndmp_pval zfs_envs[11];
944 	ulong_t zfs_attrs;
945 	ndmp_pval *zfs_envp = zfs_envs;
946 
947 
948 	(void) memset((void*)&reply, 0, sizeof (reply));
949 
950 	/*
951 	 * Supported environment variables and their default values
952 	 * for dump and tar.
953 	 *
954 	 * The environment variables for dump and tar format are the
955 	 * same, because we use the same backup engine for both.
956 	 */
957 	NDMP_SETENV(envp, "FILESYSTEM", "");
958 	NDMP_SETENV(envp, "DIRECT", "n");
959 	NDMP_SETENV(envp, "RECURSIVE", "n");
960 	NDMP_SETENV(envp, "TYPE", "");
961 	NDMP_SETENV(envp, "USER", "");
962 	NDMP_SETENV(envp, "HIST", "n");
963 	NDMP_SETENV(envp, "PATHNAME_SEPARATOR", "/");
964 	NDMP_SETENV(envp, "LEVEL", "0");
965 	NDMP_SETENV(envp, "EXTRACT", "y");
966 	NDMP_SETENV(envp, "UPDATE", "y");
967 	NDMP_SETENV(envp, "CMD", "");
968 	NDMP_SETENV(envp, "BASE_DATE", "");
969 
970 	attrs = NDMP_BUTYPE_RECOVER_FILELIST |
971 	    NDMP_BUTYPE_BACKUP_DIRECT |
972 	    NDMP_BUTYPE_BACKUP_INCREMENTAL |
973 	    NDMP_BUTYPE_BACKUP_UTF8 |
974 	    NDMP_BUTYPE_RECOVER_UTF8 |
975 	    NDMP_BUTYPE_BACKUP_FH_FILE |
976 	    NDMP_BUTYPE_BACKUP_FH_DIR |
977 	    NDMP_BUTYPE_RECOVER_FH_FILE |
978 	    NDMP_BUTYPE_RECOVER_FH_DIR;
979 
980 	/* If DAR supported */
981 	if (ndmp_dar_support)
982 		attrs |= NDMP_BUTYPE_RECOVER_DIRECT;
983 
984 	/* tar backup type */
985 	info[0].butype_name = "tar";
986 	info[0].default_env.default_env_len = ARRAY_LEN(envs, ndmp_pval);
987 	info[0].default_env.default_env_val = envs;
988 	info[0].attrs = attrs;
989 
990 	/* dump backup type */
991 	info[1].butype_name = "dump";
992 	info[1].default_env.default_env_len = ARRAY_LEN(envs, ndmp_pval);
993 	info[1].default_env.default_env_val = envs;
994 	info[1].attrs = attrs;
995 
996 	/*
997 	 * Supported environment variables and their default values
998 	 * for type "zfs."
999 	 */
1000 
1001 	NDMP_SETENV(zfs_envp, "USER", "");
1002 	NDMP_SETENV(zfs_envp, "CMD", "");
1003 	NDMP_SETENV(zfs_envp, "FILESYSTEM", "");
1004 	NDMP_SETENV(zfs_envp, "PATHNAME_SEPARATOR", "/");
1005 	NDMP_SETENV(zfs_envp, "TYPE", "zfs");
1006 	NDMP_SETENV(zfs_envp, "HIST", "n");
1007 	NDMP_SETENV(zfs_envp, "LEVEL", "0");
1008 	NDMP_SETENV(zfs_envp, "ZFS_MODE", "recursive");
1009 	NDMP_SETENV(zfs_envp, "ZFS_FORCE", "n");
1010 	NDMP_SETENV(zfs_envp, "UPDATE", "y");
1011 	NDMP_SETENV(zfs_envp, "DMP_NAME", "level");
1012 
1013 	zfs_attrs = NDMP_BUTYPE_BACKUP_UTF8 |
1014 	    NDMP_BUTYPE_RECOVER_UTF8 |
1015 	    NDMP_BUTYPE_BACKUP_DIRECT |
1016 	    NDMP_BUTYPE_BACKUP_INCREMENTAL;
1017 
1018 	/* zfs backup type */
1019 	info[2].butype_name = "zfs";
1020 	info[2].default_env.default_env_len = ARRAY_LEN(zfs_envs, ndmp_pval);
1021 	info[2].default_env.default_env_val = zfs_envs;
1022 	info[2].attrs = zfs_attrs;
1023 
1024 	reply.error = NDMP_NO_ERR;
1025 	reply.butype_info.butype_info_len = ARRAY_LEN(info, ndmp_butype_info);
1026 	reply.butype_info.butype_info_val = info;
1027 
1028 	ndmp_send_reply(connection, (void *)&reply,
1029 	    "sending ndmp_config_get_butype_info reply");
1030 }
1031 
1032 
1033 /*
1034  * ndmpd_config_get_ext_list_v4
1035  *
1036  * This handler handles the ndmpd_config_get_ext_list_v4 request.
1037  *
1038  * Parameters:
1039  *   connection (input) - connection handle.
1040  *   body       (input) - request message body.
1041  *
1042  * Returns:
1043  *   void
1044  */
1045 /*ARGSUSED*/
1046 void
1047 ndmpd_config_get_ext_list_v4(ndmp_connection_t *connection, void *body)
1048 {
1049 	ndmp_config_get_ext_list_reply_v4 reply;
1050 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1051 
1052 	(void) memset((void*)&reply, 0, sizeof (reply));
1053 
1054 	if (session->ns_set_ext_list) {
1055 		/*
1056 		 * Illegal request if extensions have already been selected.
1057 		 */
1058 		NDMP_LOG(LOG_ERR, "Extensions have already been selected.");
1059 		reply.error = NDMP_EXT_DANDN_ILLEGAL_ERR;
1060 	} else {
1061 		/*
1062 		 * Reply with an empty set of extensions.
1063 		 */
1064 		session->ns_get_ext_list = B_TRUE;
1065 		reply.error = NDMP_NO_ERR;
1066 	}
1067 
1068 	reply.class_list.class_list_val = NULL;
1069 	reply.class_list.class_list_len = 0;
1070 
1071 	ndmp_send_reply(connection, (void *)&reply,
1072 	    "error sending ndmp_config_get_ext_list reply");
1073 }
1074 
1075 /*
1076  * ndmpd_config_set_ext_list_v4
1077  *
1078  * This handler handles the ndmpd_config_get_ext_list_v4 request.
1079  *
1080  * Parameters:
1081  *   connection (input) - connection handle.
1082  *   body       (input) - request message body.
1083  *
1084  * Returns:
1085  *   void
1086  */
1087 void
1088 ndmpd_config_set_ext_list_v4(ndmp_connection_t *connection, void *body)
1089 {
1090 	ndmp_config_set_ext_list_reply_v4 reply;
1091 	ndmp_config_set_ext_list_request_v4 *request;
1092 	ndmpd_session_t *session = ndmp_get_client_data(connection);
1093 
1094 	request = (ndmp_config_set_ext_list_request_v4 *)body;
1095 
1096 	(void) memset((void*)&reply, 0, sizeof (reply));
1097 
1098 	if (!session->ns_get_ext_list) {
1099 		/*
1100 		 * The DMA is required to issue a NDMP_GET_EXT_LIST request
1101 		 * prior sending a NDMP_SET_EXT_LIST request.
1102 		 */
1103 		NDMP_LOG(LOG_ERR, "No prior ndmp_config_get_ext_list issued.");
1104 		reply.error = NDMP_PRECONDITION_ERR;
1105 	} else if (session->ns_set_ext_list) {
1106 		/*
1107 		 * Illegal request if extensions have already been selected.
1108 		 */
1109 		NDMP_LOG(LOG_ERR, "Extensions have already been selected.");
1110 		reply.error = NDMP_EXT_DANDN_ILLEGAL_ERR;
1111 	} else {
1112 		/*
1113 		 * We currently do not support any extensions, but the DMA
1114 		 * may test NDMP_CONFIG_SET_EXT_LIST with an empty list.
1115 		 */
1116 		if (request->ndmp_selected_ext.ndmp_selected_ext_len != 0) {
1117 			reply.error = NDMP_CLASS_NOT_SUPPORTED_ERR;
1118 		} else {
1119 			session->ns_set_ext_list = B_TRUE;
1120 			reply.error = NDMP_NO_ERR;
1121 		}
1122 	}
1123 
1124 	ndmp_send_reply(connection, (void *)&reply,
1125 	    "error sending ndmp_config_set_ext_list reply");
1126 }
1127 
1128 
1129 
1130 /*
1131  * ************************************************************************
1132  * LOCALS
1133  * ************************************************************************
1134  */
1135 
1136 /*
1137  * simple_get_attrs
1138  *
1139  * Set the default attrs for dump mode
1140  *
1141  * Parameters:
1142  *   attributes (output) - the attributes for dump mode
1143  *
1144  * Returns:
1145  *   void
1146  */
1147 static void
1148 simple_get_attrs(ulong_t *attributes)
1149 {
1150 	*attributes = NDMP_NO_RECOVER_FHINFO;
1151 }
1152