dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / ndmpd / ndmp / ndmpd_scsi.c
blobbdcd1741a5fffadeba1ccbb208b20e0f54734dba
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * BSD 3 Clause License
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
13 * are met:
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
20 * distribution.
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.
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>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdlib.h>
47 #include "ndmpd_common.h"
48 #include "ndmpd.h"
49 #include <string.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 * ************************************************************************
61 * NDMP V2 HANDLERS
62 * ************************************************************************
66 * ndmpd_scsi_open_v2
68 * This handler opens the specified SCSI device.
70 * Parameters:
71 * connection (input) - connection handle.
72 * body (input) - request message body.
74 * Returns:
75 * void
77 void
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);
87 * ndmpd_scsi_close_v2
89 * This handler closes the currently open SCSI device.
91 * Parameters:
92 * connection (input) - connection handle.
93 * body (input) - request message body.
95 * Returns:
96 * void
98 /*ARGSUSED*/
99 void
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");
110 return;
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
139 * SCSI devices.
141 * Parameters:
142 * connection (input) - connection handle.
143 * body (input) - request message body.
145 * Returns:
146 * void
148 /*ARGSUSED*/
149 void
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;
162 } else {
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
184 * SCSI devices.
186 * Parameters:
187 * connection (input) - connection handle.
188 * body (input) - request message body.
190 * Returns:
191 * void
193 void
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.
211 * Parameters:
212 * connection (input) - connection handle.
213 * body (input) - request message body.
215 * Returns:
216 * void
218 /*ARGSUSED*/
219 void
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;
231 } else {
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.");
237 NDMP_LOG(LOG_DEBUG,
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.
255 * Parameters:
256 * connection (input) - connection handle.
257 * body (input) - request message body.
259 * Returns:
260 * void
262 /*ARGSUSED*/
263 void
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.
281 * Parameters:
282 * connection (input) - connection handle.
283 * body (input) - request message body.
285 * Returns:
286 * void
288 void
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");
303 } else {
304 ndmp_execute_cdb(session, session->ns_scsi.sd_adapter_name,
305 session->ns_scsi.sd_sid, session->ns_scsi.sd_lun, request);
311 * ************************************************************************
312 * NDMP V3 HANDLERS
313 * ************************************************************************
317 * ndmpd_scsi_open_v3
319 * This handler opens the specified SCSI device.
321 * Parameters:
322 * connection (input) - connection handle.
323 * body (input) - request message body.
325 * Returns:
326 * void
328 void
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.
344 * Parameters:
345 * connection (input) - connection handle.
346 * body (input) - request message body.
348 * Returns:
349 * void
351 void
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 * ************************************************************************
366 * NDMP V4 HANDLERS
367 * ************************************************************************
371 * ************************************************************************
372 * LOCALS
373 * ************************************************************************
378 * scsi_open_send_reply
380 * Send a reply for SCSI open command
382 * Parameters:
383 * connection (input) - connection handle.
384 * err (input) - ndmp error code
386 * Returns:
387 * void
389 static void
390 scsi_open_send_reply(ndmp_connection_t *connection, int err)
392 ndmp_scsi_open_reply reply;
394 reply.error = err;
395 ndmp_send_reply(connection, (void *) &reply, "sending scsi_open reply");
400 * common_open
402 * Common SCSI open function for all NDMP versions
404 * Parameters:
405 * connection (input) - connection handle.
406 * devname (input) - device name to open.
408 * Returns:
409 * void
411 static void
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];
416 int sid, lun;
417 int err;
418 scsi_adapter_t *sa;
419 int devid;
421 err = NDMP_NO_ERR;
423 if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
424 NDMP_LOG(LOG_ERR,
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';
431 sid = lun = -1;
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.",
437 devname);
438 err = NDMP_NO_DEVICE_ERR;
440 } else {
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);
447 return;
450 switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) {
451 case 0:
452 /* OK */
453 break;
454 case EBUSY:
455 err = NDMP_DEVICE_BUSY_ERR;
456 break;
457 case ENOMEM:
458 err = NDMP_NO_MEM_ERR;
459 break;
460 default:
461 err = NDMP_IO_ERR;
463 if (err != NDMP_NO_ERR) {
464 scsi_open_send_reply(connection, err);
465 return;
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;
471 if (sid != -1) {
472 session->ns_scsi.sd_sid = sid;
473 session->ns_scsi.sd_lun = lun;
474 session->ns_scsi.sd_valid_target_set = TRUE;
475 } else {
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);
485 * common_set_target
487 * Set the SCSI target (SCSI number, LUN number, controller number)
489 * Parameters:
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.
496 * Returns:
497 * 0: on success
498 * -1: otherwise
500 /*ARGSUSED*/
501 static void
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);
507 int type;
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,
514 lun)) {
515 NDMP_LOG(LOG_ERR, "No such SCSI device: target %d lun %d.",
516 sid, lun);
517 reply.error = NDMP_NO_DEVICE_ERR;
518 } else {
519 type = scsi_get_devtype(session->ns_scsi.sd_adapter_name, sid,
520 lun);
521 if (type != DTYPE_SEQUENTIAL && type != DTYPE_CHANGER) {
522 NDMP_LOG(LOG_ERR,
523 "Not a tape or robot device: target %d lun %d.",
524 sid, lun);
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");
532 return;
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)) {
543 case 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);
547 break;
548 case EBUSY:
549 reply.error = NDMP_DEVICE_BUSY_ERR;
550 break;
551 case ENOMEM:
552 reply.error = NDMP_NO_MEM_ERR;
553 break;
554 default:
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");
571 * scsi_find_sid_lun
573 * gets the adapter, and returns the sid and lun number
575 void
576 scsi_find_sid_lun(scsi_adapter_t *sa, char *devname, int *sid, int *lun)
578 scsi_link_t *sl;
579 char *name;
581 for (sl = sa->sa_link_head.sl_next; sl && sl != &sa->sa_link_head;
582 sl = sl->sl_next) {
583 name = sasd_slink_name(sl);
584 if (strcmp(devname, name) == 0) {
585 *sid = sl->sl_sid;
586 *lun = sl->sl_lun;
587 return;
591 *sid = -1;
592 *lun = -1;