1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * BSD 3 Clause License
8 *
9 * Copyright (c) 2007, The Storage Networking Industry Association.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 	- Redistributions of source code must retain the above copyright
15 *	  notice, this list of conditions and the following disclaimer.
16 *
17 * 	- Redistributions in binary form must reproduce the above copyright
18 *	  notice, this list of conditions and the following disclaimer in
19 *	  the documentation and/or other materials provided with the
20 *	  distribution.
21 *
22 *	- Neither the name of The Storage Networking Industry Association (SNIA)
23 *	  nor the names of its contributors may be used to endorse or promote
24 *	  products derived from this software without specific prior written
25 *	  permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39#include <locale.h>
40#include <stdlib.h>
41#include <strings.h>
42#include <unistd.h>
43#include <fcntl.h>
44#include <door.h>
45#include <thread.h>
46#include <ndmpd_door.h>
47#include <libndmp.h>
48
49static int ndmp_door_fildes = -1;
50static char *buf;
51static ndmp_door_ctx_t *dec_ctx;
52static ndmp_door_ctx_t *enc_ctx;
53static door_arg_t arg;
54static mutex_t ndmp_lock = DEFAULTMUTEX;
55
56static int ndmp_door_setup(int opcode);
57static int ndmp_door_call(void);
58static int ndmp_door_fini(void);
59
60/* ndmp library APIs */
61int
62ndmp_get_devinfo(ndmp_devinfo_t **dip, size_t *size)
63{
64	ndmp_devinfo_t *dipptr;
65	int i;
66	int opcode = NDMP_DEVICES_GET_INFO;
67
68	(void) mutex_lock(&ndmp_lock);
69	if (ndmp_door_setup(opcode))
70		goto err;
71
72	if (ndmp_door_call())
73		goto err;
74
75	/* get the number of devices available */
76	*size = ndmp_door_get_uint32(dec_ctx);
77
78	*dip = malloc(sizeof (ndmp_devinfo_t) * *size);
79	if (!*dip) {
80		free(buf);
81		ndmp_errno = ENDMP_MEM_ALLOC;
82		goto err;
83	}
84	dipptr = *dip;
85	for (i = 0; i < *size; i++, dipptr++) {
86		dipptr->nd_dev_type = ndmp_door_get_int32(dec_ctx);
87		dipptr->nd_name = ndmp_door_get_string(dec_ctx);
88		dipptr->nd_lun = ndmp_door_get_int32(dec_ctx);
89		dipptr->nd_sid = ndmp_door_get_int32(dec_ctx);
90		dipptr->nd_vendor = ndmp_door_get_string(dec_ctx);
91		dipptr->nd_product = ndmp_door_get_string(dec_ctx);
92		dipptr->nd_revision = ndmp_door_get_string(dec_ctx);
93		dipptr->nd_serial = ndmp_door_get_string(dec_ctx);
94		dipptr->nd_wwn = ndmp_door_get_string(dec_ctx);
95	}
96	if (ndmp_door_fini()) {
97		free(*dip);
98		goto err;
99	}
100	(void) mutex_unlock(&ndmp_lock);
101	return (0);
102err:
103	(void) mutex_unlock(&ndmp_lock);
104	return (-1);
105}
106
107void
108ndmp_get_devinfo_free(ndmp_devinfo_t *dip, size_t size)
109{
110	ndmp_devinfo_t *dipptr;
111	int i;
112
113	dipptr = dip;
114	for (i = 0; i < size; i++, dipptr++) {
115		free(dipptr->nd_name);
116		free(dipptr->nd_vendor);
117		free(dipptr->nd_product);
118		free(dipptr->nd_revision);
119	}
120	free(dip);
121}
122
123int
124ndmp_terminate_session(int session)
125{
126	int ret;
127	int opcode = NDMP_TERMINATE_SESSION_ID;
128
129	(void) mutex_lock(&ndmp_lock);
130	if (ndmp_door_setup(opcode))
131		goto err;
132
133	ndmp_door_put_uint32(enc_ctx, session);
134	if (ndmp_door_call())
135		goto err;
136
137	ret = ndmp_door_get_uint32(dec_ctx);
138	if (ndmp_door_fini())
139		goto err;
140
141	(void) mutex_unlock(&ndmp_lock);
142	return (ret);
143err:
144	(void) mutex_unlock(&ndmp_lock);
145	return (-1);
146}
147
148int
149ndmp_get_session_info(ndmp_session_info_t **sinfo, size_t *size)
150{
151	int status;
152	int i, j;
153	ndmp_session_info_t *sp;
154	ndmp_dt_pval_t *ep;
155	ndmp_dt_name_t *np;
156	ndmp_dt_name_v3_t *npv3;
157	int opcode = NDMP_SHOW;
158
159	(void) mutex_lock(&ndmp_lock);
160	if (ndmp_door_setup(opcode))
161		goto err;
162
163	if (ndmp_door_call())
164		goto err;
165
166	/* number of sessions */
167	*size = ndmp_door_get_int32(dec_ctx);
168
169	*sinfo = malloc((sizeof (ndmp_session_info_t)) * *size);
170	if (!*sinfo) {
171		free(buf);
172		ndmp_errno = ENDMP_MEM_ALLOC;
173		goto err;
174	}
175	sp = *sinfo;
176	for (i = 0; i < *size; i++, sp++) {
177		status = ndmp_door_get_int32(dec_ctx);
178		if (status == NDMP_SESSION_NODATA)
179			continue;
180
181		/* connection common info */
182		sp->nsi_sid = ndmp_door_get_int32(dec_ctx);
183		sp->nsi_pver = ndmp_door_get_int32(dec_ctx);
184		sp->nsi_auth = ndmp_door_get_int32(dec_ctx);
185		sp->nsi_eof = ndmp_door_get_int32(dec_ctx);
186		sp->nsi_cl_addr = ndmp_door_get_string(dec_ctx);
187		/*
188		 * scsi and tape data are same for all version,
189		 * so keep reading
190		 */
191		/* connection common scsi info.   */
192		sp->nsi_scsi.ns_scsi_open = ndmp_door_get_int32(dec_ctx);
193		sp->nsi_scsi.ns_adapter_name = ndmp_door_get_string(dec_ctx);
194		sp->nsi_scsi.ns_valid_target_set = ndmp_door_get_int32(dec_ctx);
195		if (sp->nsi_scsi.ns_valid_target_set) {
196			sp->nsi_scsi.ns_scsi_id = ndmp_door_get_int32(dec_ctx);
197			sp->nsi_scsi.ns_lun = ndmp_door_get_int32(dec_ctx);
198		}
199
200		/* connection common tape info.   */
201		sp->nsi_tape.nt_fd = ndmp_door_get_int32(dec_ctx);
202		if (sp->nsi_tape.nt_fd != -1) {
203			sp->nsi_tape.nt_rec_count =
204			    ndmp_door_get_uint64(dec_ctx);
205			sp->nsi_tape.nt_mode = ndmp_door_get_int32(dec_ctx);
206			sp->nsi_tape.nt_dev_name =
207			    ndmp_door_get_string(dec_ctx);
208			sp->nsi_tape.nt_adapter_name =
209			    ndmp_door_get_string(dec_ctx);
210			sp->nsi_tape.nt_sid = ndmp_door_get_int32(dec_ctx);
211			sp->nsi_tape.nt_lun = ndmp_door_get_int32(dec_ctx);
212		}
213		/* all the V2 mover data are same as V3/V4 */
214		sp->nsi_mover.nm_state = ndmp_door_get_int32(dec_ctx);
215		sp->nsi_mover.nm_mode = ndmp_door_get_int32(dec_ctx);
216		sp->nsi_mover.nm_pause_reason = ndmp_door_get_int32(dec_ctx);
217		sp->nsi_mover.nm_halt_reason = ndmp_door_get_int32(dec_ctx);
218		sp->nsi_mover.nm_rec_size = ndmp_door_get_uint64(dec_ctx);
219		sp->nsi_mover.nm_rec_num = ndmp_door_get_uint64(dec_ctx);
220		sp->nsi_mover.nm_mov_pos = ndmp_door_get_uint64(dec_ctx);
221		sp->nsi_mover.nm_window_offset = ndmp_door_get_uint64(dec_ctx);
222		sp->nsi_mover.nm_window_length = ndmp_door_get_uint64(dec_ctx);
223		sp->nsi_mover.nm_sock = ndmp_door_get_int32(dec_ctx);
224
225		/* Read V3/V4 mover info */
226		if ((sp->nsi_pver == NDMP_V3) || (sp->nsi_pver == NDMP_V4)) {
227			sp->nsi_mover.nm_listen_sock =
228			    ndmp_door_get_int32(dec_ctx);
229			sp->nsi_mover.nm_addr_type =
230			    ndmp_door_get_int32(dec_ctx);
231			sp->nsi_mover.nm_tcp_addr =
232			    ndmp_door_get_string(dec_ctx);
233		}
234
235		/* connection common data info */
236		sp->nsi_data.nd_oper = ndmp_door_get_int32(dec_ctx);
237		sp->nsi_data.nd_state = ndmp_door_get_int32(dec_ctx);
238		sp->nsi_data.nd_halt_reason = ndmp_door_get_int32(dec_ctx);
239		sp->nsi_data.nd_sock = ndmp_door_get_int32(dec_ctx);
240		sp->nsi_data.nd_addr_type = ndmp_door_get_int32(dec_ctx);
241		sp->nsi_data.nd_abort = ndmp_door_get_int32(dec_ctx);
242		sp->nsi_data.nd_read_offset = ndmp_door_get_uint64(dec_ctx);
243		sp->nsi_data.nd_read_length = ndmp_door_get_uint64(dec_ctx);
244		sp->nsi_data.nd_total_size = ndmp_door_get_uint64(dec_ctx);
245		sp->nsi_data.nd_env_len = ndmp_door_get_uint64(dec_ctx);
246		sp->nsi_data.nd_env =
247		    malloc(sizeof (ndmp_dt_pval_t) * sp->nsi_data.nd_env_len);
248		if (!sp->nsi_data.nd_env) {
249			free(buf);
250			ndmp_errno = ENDMP_MEM_ALLOC;
251			goto err;
252		}
253		ep = sp->nsi_data.nd_env;
254		for (j = 0; j < sp->nsi_data.nd_env_len; j++, ep++) {
255			ep->np_name = ndmp_door_get_string(dec_ctx);
256			ep->np_value = ndmp_door_get_string(dec_ctx);
257		}
258		sp->nsi_data.nd_tcp_addr = ndmp_door_get_string(dec_ctx);
259
260		/* Read V2 data info */
261		if (sp->nsi_pver == NDMP_V2) {
262			sp->nsi_data.nld_nlist_len =
263			    ndmp_door_get_int64(dec_ctx);
264			sp->nsi_data.nd_nlist.nld_nlist =
265			    malloc(sizeof (ndmp_dt_name_t) *
266			    sp->nsi_data.nld_nlist_len);
267			if (!sp->nsi_data.nd_nlist.nld_nlist) {
268				free(buf);
269				ndmp_errno = ENDMP_MEM_ALLOC;
270				goto err;
271			}
272			np = sp->nsi_data.nd_nlist.nld_nlist;
273
274			for (j = 0; j < sp->nsi_data.nld_nlist_len; j++, np++) {
275				np->nn_name = ndmp_door_get_string(dec_ctx);
276				np->nn_dest = ndmp_door_get_string(dec_ctx);
277			}
278		} else if ((sp->nsi_pver == NDMP_V3) ||
279		    (sp->nsi_pver == NDMP_V4)) {
280			/* Read V3/V4 data info */
281			sp->nsi_data.nd_nlist.nld_dt_v3.dv3_listen_sock =
282			    ndmp_door_get_int32(dec_ctx);
283			sp->nsi_data.nd_nlist.nld_dt_v3.dv3_bytes_processed =
284			    ndmp_door_get_uint64(dec_ctx);
285			sp->nsi_data.nld_nlist_len =
286			    ndmp_door_get_uint64(dec_ctx);
287			sp->nsi_data.nd_nlist.nld_dt_v3.dv3_nlist =
288			    malloc(sizeof (ndmp_dt_name_v3_t) *
289			    sp->nsi_data.nld_nlist_len);
290			if (!sp->nsi_data.nd_nlist.nld_dt_v3.dv3_nlist) {
291				free(buf);
292				ndmp_errno = ENDMP_MEM_ALLOC;
293				goto err;
294			}
295			npv3 = sp->nsi_data.nd_nlist.nld_dt_v3.dv3_nlist;
296			for (j = 0; j < sp->nsi_data.nld_nlist_len;
297			    j++, npv3++) {
298				npv3->nn3_opath = ndmp_door_get_string(dec_ctx);
299				npv3->nn3_dpath = ndmp_door_get_string(dec_ctx);
300				npv3->nn3_node = ndmp_door_get_uint64(dec_ctx);
301				npv3->nn3_fh_info =
302				    ndmp_door_get_uint64(dec_ctx);
303			}
304		}
305	}
306
307	if (ndmp_door_fini())
308		goto err;
309
310	(void) mutex_unlock(&ndmp_lock);
311	return (0);
312err:
313	(void) mutex_unlock(&ndmp_lock);
314	return (-1);
315}
316
317void
318ndmp_get_session_info_free(ndmp_session_info_t *sinfo, size_t size)
319{
320	ndmp_session_info_t *sp;
321	ndmp_dt_pval_t *ep;
322	ndmp_dt_name_t *np;
323	ndmp_dt_name_v3_t *npv3;
324	int i, j;
325
326	sp = sinfo;
327	for (i = 0; i < size; i++, sp++) {
328		free(sp->nsi_cl_addr);
329		free(sp->nsi_scsi.ns_adapter_name);
330		if (sp->nsi_tape.nt_fd != -1) {
331			free(sp->nsi_tape.nt_dev_name);
332			free(sp->nsi_tape.nt_adapter_name);
333		}
334		if ((sp->nsi_pver == NDMP_V3) || (sp->nsi_pver == NDMP_V4))
335			free(sp->nsi_mover.nm_tcp_addr);
336
337		ep = sp->nsi_data.nd_env;
338		for (j = 0; j < sp->nsi_data.nd_env_len; j++, ep++) {
339			free(ep->np_name);
340			free(ep->np_value);
341		}
342		free(sp->nsi_data.nd_env);
343		free(sp->nsi_data.nd_tcp_addr);
344
345		if (sp->nsi_pver == NDMP_V2) {
346			np = sp->nsi_data.nd_nlist.nld_nlist;
347			for (j = 0; j < sp->nsi_data.nld_nlist_len; j++, np++) {
348				free(np->nn_name);
349				free(np->nn_dest);
350			}
351			free(sp->nsi_data.nd_nlist.nld_nlist);
352		} else if ((sp->nsi_pver == NDMP_V3) ||
353		    (sp->nsi_pver == NDMP_V4)) {
354			npv3 = sp->nsi_data.nd_nlist.nld_dt_v3.dv3_nlist;
355			for (j = 0; j < sp->nsi_data.nld_nlist_len;
356			    j++, npv3++) {
357				free(npv3->nn3_opath);
358				free(npv3->nn3_dpath);
359			}
360			free(sp->nsi_data.nd_nlist.nld_dt_v3.dv3_nlist);
361		}
362	}
363	free(sinfo);
364}
365
366/* ARGSUSED */
367int
368ndmp_get_stats(ndmp_stat_t *statp)
369{
370	int opcode = NDMP_GET_STAT;
371
372	(void) mutex_lock(&ndmp_lock);
373	if (!statp) {
374		ndmp_errno = ENDMP_INVALID_ARG;
375		goto err;
376	}
377
378	if (ndmp_door_setup(opcode))
379		goto err;
380
381	if (ndmp_door_call())
382		goto err;
383
384	statp->ns_trun = ndmp_door_get_uint32(dec_ctx);
385	statp->ns_twait = ndmp_door_get_uint32(dec_ctx);
386	statp->ns_nbk = ndmp_door_get_uint32(dec_ctx);
387	statp->ns_nrs = ndmp_door_get_uint32(dec_ctx);
388	statp->ns_rfile = ndmp_door_get_uint32(dec_ctx);
389	statp->ns_wfile = ndmp_door_get_uint32(dec_ctx);
390	statp->ns_rdisk = ndmp_door_get_uint64(dec_ctx);
391	statp->ns_wdisk = ndmp_door_get_uint64(dec_ctx);
392	statp->ns_rtape = ndmp_door_get_uint64(dec_ctx);
393	statp->ns_wtape = ndmp_door_get_uint64(dec_ctx);
394
395	if (ndmp_door_fini())
396		goto err;
397
398	(void) mutex_unlock(&ndmp_lock);
399	return (0);
400err:
401	(void) mutex_unlock(&ndmp_lock);
402	return (-1);
403}
404
405int
406ndmp_door_status(void)
407{
408	int opcode = NDMP_GET_DOOR_STATUS;
409
410	(void) mutex_lock(&ndmp_lock);
411	if (ndmp_door_setup(opcode))
412		goto err;
413
414	if (ndmp_door_call())
415		goto err;
416
417	if (ndmp_door_fini())
418		goto err;
419
420	(void) mutex_unlock(&ndmp_lock);
421	return (0);
422err:
423	(void) mutex_unlock(&ndmp_lock);
424	return (-1);
425}
426
427static int
428ndmp_door_setup(int opcode)
429{
430	/* Open channel to NDMP service */
431	if ((ndmp_door_fildes == -1) &&
432	    (ndmp_door_fildes = open(NDMP_DOOR_SVC, O_RDONLY)) < 0) {
433		ndmp_errno = ENDMP_DOOR_OPEN;
434		return (-1);
435	}
436
437	buf = malloc(NDMP_DOOR_SIZE);
438	if (!buf) {
439		ndmp_errno = ENDMP_MEM_ALLOC;
440		return (-1);
441	}
442
443	enc_ctx = ndmp_door_encode_start(buf, NDMP_DOOR_SIZE);
444	if (enc_ctx == 0) {
445		free(buf);
446		ndmp_errno = ENDMP_DOOR_ENCODE_START;
447		return (-1);
448	}
449	ndmp_door_put_uint32(enc_ctx, opcode);
450	return (0);
451}
452
453static int
454ndmp_door_call(void)
455{
456	uint32_t used;
457	int rc;
458
459	if ((ndmp_door_encode_finish(enc_ctx, &used)) != 0) {
460		free(buf);
461		ndmp_errno = ENDMP_DOOR_ENCODE_FINISH;
462		return (-1);
463	}
464
465	arg.data_ptr = buf;
466	arg.data_size = used;
467	arg.desc_ptr = NULL;
468	arg.desc_num = 0;
469	arg.rbuf = buf;
470	arg.rsize = NDMP_DOOR_SIZE;
471
472	if (door_call(ndmp_door_fildes, &arg) < 0) {
473		free(buf);
474		ndmp_errno = ENDMP_DOOR_SRV_TIMEOUT;
475		(void) close(ndmp_door_fildes);
476		ndmp_door_fildes = -1;
477		return (-1);
478	}
479
480	dec_ctx = ndmp_door_decode_start(arg.data_ptr, arg.data_size);
481	rc = ndmp_door_get_uint32(dec_ctx);
482	if (rc != NDMP_DOOR_SRV_SUCCESS) {
483		free(buf);
484		ndmp_errno = ENDMP_DOOR_SRV_OPERATION;
485		return (-1);
486	}
487	return (0);
488}
489
490static int
491ndmp_door_fini(void)
492{
493	if ((ndmp_door_decode_finish(dec_ctx)) != 0) {
494		free(buf);
495		ndmp_errno = ENDMP_DOOR_DECODE_FINISH;
496		return (-1);
497	}
498	free(buf);
499	return (0);
500}
501