2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
9 * Copyright (c) 2007, The Storage Networking Industry Association.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * - Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
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
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
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.
39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
42 #include <sys/types.h>
47 #include "ndmpd_common.h"
50 #include <sys/scsi/impl/uscsi.h>
51 #include <sys/scsi/scsi.h>
53 static void scsi_open_send_reply(ndmp_connection_t
*connection
, int err
);
54 static void common_open(ndmp_connection_t
*connection
, char *devname
);
55 static void common_set_target(ndmp_connection_t
*connection
, char *device
,
56 ushort_t controller
, ushort_t sid
, ushort_t lun
);
60 * ************************************************************************
62 * ************************************************************************
68 * This handler opens the specified SCSI device.
71 * connection (input) - connection handle.
72 * body (input) - request message body.
78 ndmpd_scsi_open_v2(ndmp_connection_t
*connection
, void *body
)
80 ndmp_scsi_open_request_v2
*request
= (ndmp_scsi_open_request_v2
*)body
;
82 common_open(connection
, request
->device
.name
);
89 * This handler closes the currently open SCSI device.
92 * connection (input) - connection handle.
93 * body (input) - request message body.
100 ndmpd_scsi_close_v2(ndmp_connection_t
*connection
, void *body
)
102 ndmp_scsi_close_reply reply
;
103 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
105 if (session
->ns_scsi
.sd_is_open
== -1) {
106 NDMP_LOG(LOG_ERR
, "SCSI device is not open.");
107 reply
.error
= NDMP_DEV_NOT_OPEN_ERR
;
108 ndmp_send_reply(connection
, (void *) &reply
,
109 "sending scsi_close reply");
112 (void) ndmp_open_list_del(session
->ns_scsi
.sd_adapter_name
,
113 session
->ns_scsi
.sd_sid
,
114 session
->ns_scsi
.sd_lun
);
115 (void) close(session
->ns_scsi
.sd_devid
);
117 session
->ns_scsi
.sd_is_open
= -1;
118 session
->ns_scsi
.sd_devid
= -1;
119 session
->ns_scsi
.sd_sid
= 0;
120 session
->ns_scsi
.sd_lun
= 0;
121 session
->ns_scsi
.sd_valid_target_set
= FALSE
;
122 (void) memset(session
->ns_scsi
.sd_adapter_name
, 0,
123 sizeof (session
->ns_scsi
.sd_adapter_name
));
125 reply
.error
= NDMP_NO_ERR
;
126 ndmp_send_reply(connection
, (void *) &reply
,
127 "sending scsi_close reply");
132 * ndmpd_scsi_get_state_v2
134 * This handler returns state information for the currently open SCSI device.
135 * Since the implementation only supports the opening of a specific SCSI
136 * device, as opposed to a device that can talk to multiple SCSI targets,
137 * this request is not supported. This request is only appropriate for
138 * implementations that support device files that can target multiple
142 * connection (input) - connection handle.
143 * body (input) - request message body.
150 ndmpd_scsi_get_state_v2(ndmp_connection_t
*connection
, void *body
)
152 ndmp_scsi_get_state_reply reply
;
153 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
155 if (session
->ns_scsi
.sd_is_open
== -1)
156 reply
.error
= NDMP_DEV_NOT_OPEN_ERR
;
157 else if (!session
->ns_scsi
.sd_valid_target_set
) {
158 reply
.error
= NDMP_NO_ERR
;
159 reply
.target_controller
= -1;
160 reply
.target_id
= -1;
161 reply
.target_lun
= -1;
163 reply
.error
= NDMP_NO_ERR
;
164 reply
.target_controller
= 0;
165 reply
.target_id
= session
->ns_scsi
.sd_sid
;
166 reply
.target_lun
= session
->ns_scsi
.sd_lun
;
169 ndmp_send_reply(connection
, (void *) &reply
,
170 "sending scsi_get_state reply");
175 * ndmpd_scsi_set_target_v2
177 * This handler sets the SCSI target of the SCSI device.
178 * It is only valid to use this request if the opened SCSI device
179 * is capable of talking to multiple SCSI targets.
180 * Since the implementation only supports the opening of a specific SCSI
181 * device, as opposed to a device that can talk to multiple SCSI targets,
182 * this request is not supported. This request is only appropriate for
183 * implementations that support device files that can target multiple
187 * connection (input) - connection handle.
188 * body (input) - request message body.
194 ndmpd_scsi_set_target_v2(ndmp_connection_t
*connection
, void *body
)
196 ndmp_scsi_set_target_request_v2
*request
;
198 request
= (ndmp_scsi_set_target_request_v2
*) body
;
200 common_set_target(connection
, request
->device
.name
,
201 request
->target_controller
, request
->target_id
,
202 request
->target_lun
);
207 * ndmpd_scsi_reset_device_v2
209 * This handler resets the currently targeted SCSI device.
212 * connection (input) - connection handle.
213 * body (input) - request message body.
220 ndmpd_scsi_reset_device_v2(ndmp_connection_t
*connection
, void *body
)
222 ndmp_scsi_reset_device_reply reply
;
225 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
226 struct uscsi_cmd cmd
;
228 if (session
->ns_scsi
.sd_devid
== -1) {
229 NDMP_LOG(LOG_ERR
, "SCSI device is not open.");
230 reply
.error
= NDMP_DEV_NOT_OPEN_ERR
;
232 reply
.error
= NDMP_NO_ERR
;
233 (void) memset(&cmd
, 0, sizeof (cmd
));
234 cmd
.uscsi_flags
|= USCSI_RESET
;
235 if (ioctl(session
->ns_scsi
.sd_devid
, USCSICMD
, &cmd
) < 0) {
236 NDMP_LOG(LOG_ERR
, "USCSI reset failed: %m.");
238 "ioctl(USCSICMD) USCSI_RESET failed: %m.");
239 reply
.error
= NDMP_IO_ERR
;
243 ndmp_send_reply(connection
, (void *) &reply
,
244 "sending scsi_reset_device reply");
249 * ndmpd_scsi_reset_bus_v2
251 * This handler resets the currently targeted SCSI bus.
253 * Request not yet supported.
256 * connection (input) - connection handle.
257 * body (input) - request message body.
264 ndmpd_scsi_reset_bus_v2(ndmp_connection_t
*connection
, void *body
)
266 ndmp_scsi_reset_bus_reply reply
;
268 NDMP_LOG(LOG_DEBUG
, "request not supported");
269 reply
.error
= NDMP_NOT_SUPPORTED_ERR
;
271 ndmp_send_reply(connection
, (void *) &reply
,
272 "sending scsi_reset_bus reply");
277 * ndmpd_scsi_execute_cdb_v2
279 * This handler sends the CDB to the currently targeted SCSI device.
282 * connection (input) - connection handle.
283 * body (input) - request message body.
289 ndmpd_scsi_execute_cdb_v2(ndmp_connection_t
*connection
, void *body
)
291 ndmp_execute_cdb_request
*request
= (ndmp_execute_cdb_request
*) body
;
292 ndmp_execute_cdb_reply reply
;
293 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
295 if (session
->ns_scsi
.sd_is_open
== -1 ||
296 !session
->ns_scsi
.sd_valid_target_set
) {
297 (void) memset(&reply
, 0, sizeof (reply
));
299 NDMP_LOG(LOG_ERR
, "SCSI device is not open.");
300 reply
.error
= NDMP_DEV_NOT_OPEN_ERR
;
301 ndmp_send_reply(connection
, (void *) &reply
,
302 "sending scsi_execute_cdb reply");
304 ndmp_execute_cdb(session
, session
->ns_scsi
.sd_adapter_name
,
305 session
->ns_scsi
.sd_sid
, session
->ns_scsi
.sd_lun
, request
);
311 * ************************************************************************
313 * ************************************************************************
319 * This handler opens the specified SCSI device.
322 * connection (input) - connection handle.
323 * body (input) - request message body.
329 ndmpd_scsi_open_v3(ndmp_connection_t
*connection
, void *body
)
331 ndmp_scsi_open_request_v3
*request
= (ndmp_scsi_open_request_v3
*)body
;
333 common_open(connection
, request
->device
);
338 * ndmpd_scsi_set_target_v3
340 * This handler sets the SCSI target of the SCSI device.
341 * It is only valid to use this request if the opened SCSI device
342 * is capable of talking to multiple SCSI targets.
345 * connection (input) - connection handle.
346 * body (input) - request message body.
352 ndmpd_scsi_set_target_v3(ndmp_connection_t
*connection
, void *body
)
354 ndmp_scsi_set_target_request_v3
*request
;
356 request
= (ndmp_scsi_set_target_request_v3
*) body
;
358 common_set_target(connection
, request
->device
,
359 request
->target_controller
, request
->target_id
,
360 request
->target_lun
);
365 * ************************************************************************
367 * ************************************************************************
371 * ************************************************************************
373 * ************************************************************************
378 * scsi_open_send_reply
380 * Send a reply for SCSI open command
383 * connection (input) - connection handle.
384 * err (input) - ndmp error code
390 scsi_open_send_reply(ndmp_connection_t
*connection
, int err
)
392 ndmp_scsi_open_reply reply
;
395 ndmp_send_reply(connection
, (void *) &reply
, "sending scsi_open reply");
402 * Common SCSI open function for all NDMP versions
405 * connection (input) - connection handle.
406 * devname (input) - device name to open.
412 common_open(ndmp_connection_t
*connection
, char *devname
)
414 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
415 char adptnm
[SCSI_MAX_NAME
];
423 if (session
->ns_tape
.td_fd
!= -1 || session
->ns_scsi
.sd_is_open
!= -1) {
425 "Session already has a tape or scsi device open.");
426 err
= NDMP_DEVICE_OPENED_ERR
;
427 } else if ((sa
= scsi_get_adapter(0)) != NULL
) {
428 NDMP_LOG(LOG_DEBUG
, "Adapter device found: %s", devname
);
429 (void) strlcpy(adptnm
, devname
, SCSI_MAX_NAME
-2);
430 adptnm
[SCSI_MAX_NAME
-1] = '\0';
433 scsi_find_sid_lun(sa
, devname
, &sid
, &lun
);
434 if (ndmp_open_list_find(devname
, sid
, lun
) == NULL
&&
435 (devid
= open(devname
, O_RDWR
| O_NDELAY
)) < 0) {
436 NDMP_LOG(LOG_ERR
, "Failed to open device %s: %m.",
438 err
= NDMP_NO_DEVICE_ERR
;
441 NDMP_LOG(LOG_ERR
, "%s: No such SCSI adapter.", devname
);
442 err
= NDMP_NO_DEVICE_ERR
;
445 if (err
!= NDMP_NO_ERR
) {
446 scsi_open_send_reply(connection
, err
);
450 switch (ndmp_open_list_add(connection
, adptnm
, sid
, lun
, devid
)) {
455 err
= NDMP_DEVICE_BUSY_ERR
;
458 err
= NDMP_NO_MEM_ERR
;
463 if (err
!= NDMP_NO_ERR
) {
464 scsi_open_send_reply(connection
, err
);
468 (void) strlcpy(session
->ns_scsi
.sd_adapter_name
, adptnm
, SCSI_MAX_NAME
);
469 session
->ns_scsi
.sd_is_open
= 1;
470 session
->ns_scsi
.sd_devid
= devid
;
472 session
->ns_scsi
.sd_sid
= sid
;
473 session
->ns_scsi
.sd_lun
= lun
;
474 session
->ns_scsi
.sd_valid_target_set
= TRUE
;
476 session
->ns_scsi
.sd_sid
= session
->ns_scsi
.sd_lun
= -1;
477 session
->ns_scsi
.sd_valid_target_set
= FALSE
;
480 scsi_open_send_reply(connection
, err
);
487 * Set the SCSI target (SCSI number, LUN number, controller number)
490 * connection (input) - connection handle.
491 * device (input) - device name.
492 * controller (input) - controller number.
493 * sid (input) - SCSI target ID.
494 * lun (input) - LUN number.
502 common_set_target(ndmp_connection_t
*connection
, char *device
,
503 ushort_t controller
, ushort_t sid
, ushort_t lun
)
505 ndmp_scsi_set_target_reply reply
;
506 ndmpd_session_t
*session
= ndmp_get_client_data(connection
);
509 reply
.error
= NDMP_NO_ERR
;
511 if (session
->ns_scsi
.sd_is_open
== -1) {
512 reply
.error
= NDMP_DEV_NOT_OPEN_ERR
;
513 } else if (!scsi_dev_exists(session
->ns_scsi
.sd_adapter_name
, sid
,
515 NDMP_LOG(LOG_ERR
, "No such SCSI device: target %d lun %d.",
517 reply
.error
= NDMP_NO_DEVICE_ERR
;
519 type
= scsi_get_devtype(session
->ns_scsi
.sd_adapter_name
, sid
,
521 if (type
!= DTYPE_SEQUENTIAL
&& type
!= DTYPE_CHANGER
) {
523 "Not a tape or robot device: target %d lun %d.",
525 reply
.error
= NDMP_ILLEGAL_ARGS_ERR
;
529 if (reply
.error
!= NDMP_NO_ERR
) {
530 ndmp_send_reply(connection
, (void *) &reply
,
531 "sending scsi_set_target reply");
536 * The open_list must be updated if the SID or LUN are going to be
537 * changed. Close uses the same SID & LUN for removing the entry
538 * from the open_list.
540 if (sid
!= session
->ns_scsi
.sd_sid
|| lun
!= session
->ns_scsi
.sd_lun
) {
541 switch (ndmp_open_list_add(connection
,
542 session
->ns_scsi
.sd_adapter_name
, sid
, lun
, 0)) {
544 (void) ndmp_open_list_del(session
->
545 ns_scsi
.sd_adapter_name
, session
->ns_scsi
.sd_sid
,
546 session
->ns_scsi
.sd_lun
);
549 reply
.error
= NDMP_DEVICE_BUSY_ERR
;
552 reply
.error
= NDMP_NO_MEM_ERR
;
555 reply
.error
= NDMP_IO_ERR
;
559 if (reply
.error
== NDMP_NO_ERR
) {
560 NDMP_LOG(LOG_DEBUG
, "Updated sid %d lun %d", sid
, lun
);
561 session
->ns_scsi
.sd_sid
= sid
;
562 session
->ns_scsi
.sd_lun
= lun
;
563 session
->ns_scsi
.sd_valid_target_set
= TRUE
;
566 ndmp_send_reply(connection
, (void *) &reply
,
567 "sending scsi_set_target reply");
573 * gets the adapter, and returns the sid and lun number
576 scsi_find_sid_lun(scsi_adapter_t
*sa
, char *devname
, int *sid
, int *lun
)
581 for (sl
= sa
->sa_link_head
.sl_next
; sl
&& sl
!= &sa
->sa_link_head
;
583 name
= sasd_slink_name(sl
);
584 if (strcmp(devname
, name
) == 0) {