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 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
41
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>
52
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);
57
58
59 /*
60 * ************************************************************************
61 * NDMP V2 HANDLERS
62 * ************************************************************************
63 */
64
65 /*
66 * ndmpd_scsi_open_v2
67 *
68 * This handler opens the specified SCSI device.
69 *
70 * Parameters:
71 * connection (input) - connection handle.
72 * body (input) - request message body.
73 *
74 * Returns:
75 * void
76 */
77 void
ndmpd_scsi_open_v2(ndmp_connection_t * connection,void * body)78 ndmpd_scsi_open_v2(ndmp_connection_t *connection, void *body)
79 {
80 ndmp_scsi_open_request_v2 *request = (ndmp_scsi_open_request_v2 *)body;
81
82 common_open(connection, request->device.name);
83 }
84
85
86 /*
87 * ndmpd_scsi_close_v2
88 *
89 * This handler closes the currently open SCSI device.
90 *
91 * Parameters:
92 * connection (input) - connection handle.
93 * body (input) - request message body.
94 *
95 * Returns:
96 * void
97 */
98 /*ARGSUSED*/
99 void
ndmpd_scsi_close_v2(ndmp_connection_t * connection,void * body)100 ndmpd_scsi_close_v2(ndmp_connection_t *connection, void *body)
101 {
102 ndmp_scsi_close_reply reply;
103 ndmpd_session_t *session = ndmp_get_client_data(connection);
104
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;
111 }
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);
116
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));
124
125 reply.error = NDMP_NO_ERR;
126 ndmp_send_reply(connection, (void *) &reply,
127 "sending scsi_close reply");
128 }
129
130
131 /*
132 * ndmpd_scsi_get_state_v2
133 *
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.
140 *
141 * Parameters:
142 * connection (input) - connection handle.
143 * body (input) - request message body.
144 *
145 * Returns:
146 * void
147 */
148 /*ARGSUSED*/
149 void
ndmpd_scsi_get_state_v2(ndmp_connection_t * connection,void * body)150 ndmpd_scsi_get_state_v2(ndmp_connection_t *connection, void *body)
151 {
152 ndmp_scsi_get_state_reply reply;
153 ndmpd_session_t *session = ndmp_get_client_data(connection);
154
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;
167 }
168
169 ndmp_send_reply(connection, (void *) &reply,
170 "sending scsi_get_state reply");
171 }
172
173
174 /*
175 * ndmpd_scsi_set_target_v2
176 *
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.
185 *
186 * Parameters:
187 * connection (input) - connection handle.
188 * body (input) - request message body.
189 *
190 * Returns:
191 * void
192 */
193 void
ndmpd_scsi_set_target_v2(ndmp_connection_t * connection,void * body)194 ndmpd_scsi_set_target_v2(ndmp_connection_t *connection, void *body)
195 {
196 ndmp_scsi_set_target_request_v2 *request;
197
198 request = (ndmp_scsi_set_target_request_v2 *) body;
199
200 common_set_target(connection, request->device.name,
201 request->target_controller, request->target_id,
202 request->target_lun);
203 }
204
205
206 /*
207 * ndmpd_scsi_reset_device_v2
208 *
209 * This handler resets the currently targeted SCSI device.
210 *
211 * Parameters:
212 * connection (input) - connection handle.
213 * body (input) - request message body.
214 *
215 * Returns:
216 * void
217 */
218 /*ARGSUSED*/
219 void
ndmpd_scsi_reset_device_v2(ndmp_connection_t * connection,void * body)220 ndmpd_scsi_reset_device_v2(ndmp_connection_t *connection, void *body)
221 {
222 ndmp_scsi_reset_device_reply reply;
223
224
225 ndmpd_session_t *session = ndmp_get_client_data(connection);
226 struct uscsi_cmd cmd;
227
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((void*)&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;
240 }
241 }
242
243 ndmp_send_reply(connection, (void *) &reply,
244 "sending scsi_reset_device reply");
245 }
246
247
248 /*
249 * ndmpd_scsi_reset_bus_v2
250 *
251 * This handler resets the currently targeted SCSI bus.
252 *
253 * Request not yet supported.
254 *
255 * Parameters:
256 * connection (input) - connection handle.
257 * body (input) - request message body.
258 *
259 * Returns:
260 * void
261 */
262 /*ARGSUSED*/
263 void
ndmpd_scsi_reset_bus_v2(ndmp_connection_t * connection,void * body)264 ndmpd_scsi_reset_bus_v2(ndmp_connection_t *connection, void *body)
265 {
266 ndmp_scsi_reset_bus_reply reply;
267
268 NDMP_LOG(LOG_DEBUG, "request not supported");
269 reply.error = NDMP_NOT_SUPPORTED_ERR;
270
271 ndmp_send_reply(connection, (void *) &reply,
272 "sending scsi_reset_bus reply");
273 }
274
275
276 /*
277 * ndmpd_scsi_execute_cdb_v2
278 *
279 * This handler sends the CDB to the currently targeted SCSI device.
280 *
281 * Parameters:
282 * connection (input) - connection handle.
283 * body (input) - request message body.
284 *
285 * Returns:
286 * void
287 */
288 void
ndmpd_scsi_execute_cdb_v2(ndmp_connection_t * connection,void * body)289 ndmpd_scsi_execute_cdb_v2(ndmp_connection_t *connection, void *body)
290 {
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);
294
295 if (session->ns_scsi.sd_is_open == -1 ||
296 !session->ns_scsi.sd_valid_target_set) {
297 (void) memset((void *) &reply, 0, sizeof (reply));
298
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);
306 }
307 }
308
309
310 /*
311 * ************************************************************************
312 * NDMP V3 HANDLERS
313 * ************************************************************************
314 */
315
316 /*
317 * ndmpd_scsi_open_v3
318 *
319 * This handler opens the specified SCSI device.
320 *
321 * Parameters:
322 * connection (input) - connection handle.
323 * body (input) - request message body.
324 *
325 * Returns:
326 * void
327 */
328 void
ndmpd_scsi_open_v3(ndmp_connection_t * connection,void * body)329 ndmpd_scsi_open_v3(ndmp_connection_t *connection, void *body)
330 {
331 ndmp_scsi_open_request_v3 *request = (ndmp_scsi_open_request_v3 *)body;
332
333 common_open(connection, request->device);
334 }
335
336
337 /*
338 * ndmpd_scsi_set_target_v3
339 *
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.
343 *
344 * Parameters:
345 * connection (input) - connection handle.
346 * body (input) - request message body.
347 *
348 * Returns:
349 * void
350 */
351 void
ndmpd_scsi_set_target_v3(ndmp_connection_t * connection,void * body)352 ndmpd_scsi_set_target_v3(ndmp_connection_t *connection, void *body)
353 {
354 ndmp_scsi_set_target_request_v3 *request;
355
356 request = (ndmp_scsi_set_target_request_v3 *) body;
357
358 common_set_target(connection, request->device,
359 request->target_controller, request->target_id,
360 request->target_lun);
361 }
362
363
364 /*
365 * ************************************************************************
366 * NDMP V4 HANDLERS
367 * ************************************************************************
368 */
369
370 /*
371 * ************************************************************************
372 * LOCALS
373 * ************************************************************************
374 */
375
376
377 /*
378 * scsi_open_send_reply
379 *
380 * Send a reply for SCSI open command
381 *
382 * Parameters:
383 * connection (input) - connection handle.
384 * err (input) - ndmp error code
385 *
386 * Returns:
387 * void
388 */
389 static void
scsi_open_send_reply(ndmp_connection_t * connection,int err)390 scsi_open_send_reply(ndmp_connection_t *connection, int err)
391 {
392 ndmp_scsi_open_reply reply;
393
394 reply.error = err;
395 ndmp_send_reply(connection, (void *) &reply, "sending scsi_open reply");
396 }
397
398
399 /*
400 * common_open
401 *
402 * Common SCSI open function for all NDMP versions
403 *
404 * Parameters:
405 * connection (input) - connection handle.
406 * devname (input) - device name to open.
407 *
408 * Returns:
409 * void
410 */
411 static void
common_open(ndmp_connection_t * connection,char * devname)412 common_open(ndmp_connection_t *connection, char *devname)
413 {
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;
420
421 err = NDMP_NO_ERR;
422
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;
432
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;
439 }
440 } else {
441 NDMP_LOG(LOG_ERR, "%s: No such SCSI adapter.", devname);
442 err = NDMP_NO_DEVICE_ERR;
443 }
444
445 if (err != NDMP_NO_ERR) {
446 scsi_open_send_reply(connection, err);
447 return;
448 }
449
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;
462 }
463 if (err != NDMP_NO_ERR) {
464 scsi_open_send_reply(connection, err);
465 return;
466 }
467
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;
478 }
479
480 scsi_open_send_reply(connection, err);
481 }
482
483
484 /*
485 * common_set_target
486 *
487 * Set the SCSI target (SCSI number, LUN number, controller number)
488 *
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.
495 *
496 * Returns:
497 * 0: on success
498 * -1: otherwise
499 */
500 /*ARGSUSED*/
501 static void
common_set_target(ndmp_connection_t * connection,char * device,ushort_t controller,ushort_t sid,ushort_t lun)502 common_set_target(ndmp_connection_t *connection, char *device,
503 ushort_t controller, ushort_t sid, ushort_t lun)
504 {
505 ndmp_scsi_set_target_reply reply;
506 ndmpd_session_t *session = ndmp_get_client_data(connection);
507 int type;
508
509 reply.error = NDMP_NO_ERR;
510
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;
526 }
527 }
528
529 if (reply.error != NDMP_NO_ERR) {
530 ndmp_send_reply(connection, (void *) &reply,
531 "sending scsi_set_target reply");
532 return;
533 }
534
535 /*
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.
539 */
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;
556 }
557 }
558
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;
564 }
565
566 ndmp_send_reply(connection, (void *) &reply,
567 "sending scsi_set_target reply");
568 }
569
570 /*
571 * scsi_find_sid_lun
572 *
573 * gets the adapter, and returns the sid and lun number
574 */
575 void
scsi_find_sid_lun(scsi_adapter_t * sa,char * devname,int * sid,int * lun)576 scsi_find_sid_lun(scsi_adapter_t *sa, char *devname, int *sid, int *lun)
577 {
578 scsi_link_t *sl;
579 char *name;
580
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;
588 }
589 }
590
591 *sid = -1;
592 *lun = -1;
593 }
594