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 /* Copyright 2017 Nexenta Systems, Inc. All rights reserved. */
42
43 #include <sys/types.h>
44 #include <syslog.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdlib.h>
49 #include "ndmpd_common.h"
50 #include "ndmpd.h"
51 #include <string.h>
52 #include <sys/scsi/impl/uscsi.h>
53 #include <sys/scsi/scsi.h>
54
55 static void scsi_open_send_reply(ndmp_connection_t *connection, int err);
56 static void common_open(ndmp_connection_t *connection, char *devname);
57 static void common_set_target(ndmp_connection_t *connection, char *device,
58 ushort_t controller, ushort_t sid, ushort_t lun);
59
60
61 /*
62 * ************************************************************************
63 * NDMP V2 HANDLERS
64 * ************************************************************************
65 */
66
67 /*
68 * ndmpd_scsi_open_v2
69 *
70 * This handler opens the specified SCSI device.
71 *
72 * Parameters:
73 * connection (input) - connection handle.
74 * body (input) - request message body.
75 *
76 * Returns:
77 * void
78 */
79 void
80 ndmpd_scsi_open_v2(ndmp_connection_t *connection, void *body)
81 {
82 ndmp_scsi_open_request_v2 *request = (ndmp_scsi_open_request_v2 *)body;
83
84 common_open(connection, request->device.name);
85 }
86
87
88 /*
89 * ndmpd_scsi_close_v2
90 *
91 * This handler closes the currently open SCSI device.
92 *
93 * Parameters:
94 * connection (input) - connection handle.
95 * body (input) - request message body.
96 *
97 * Returns:
98 * void
99 */
100 /*ARGSUSED*/
101 void
102 ndmpd_scsi_close_v2(ndmp_connection_t *connection, void *body)
103 {
104 ndmp_scsi_close_reply reply;
105 ndmpd_session_t *session = ndmp_get_client_data(connection);
106
107 if (session->ns_scsi.sd_is_open == -1) {
108 syslog(LOG_ERR, "SCSI device is not open.");
109 reply.error = NDMP_DEV_NOT_OPEN_ERR;
110 ndmp_send_reply(connection, (void *) &reply,
111 "sending scsi_close reply");
112 return;
113 }
114 (void) ndmp_open_list_del(session->ns_scsi.sd_adapter_name,
115 session->ns_scsi.sd_sid,
116 session->ns_scsi.sd_lun);
117 (void) close(session->ns_scsi.sd_devid);
118
119 session->ns_scsi.sd_is_open = -1;
120 session->ns_scsi.sd_devid = -1;
121 session->ns_scsi.sd_sid = 0;
122 session->ns_scsi.sd_lun = 0;
123 session->ns_scsi.sd_valid_target_set = FALSE;
124 (void) memset(session->ns_scsi.sd_adapter_name, 0,
125 sizeof (session->ns_scsi.sd_adapter_name));
126
127 reply.error = NDMP_NO_ERR;
128 ndmp_send_reply(connection, (void *) &reply,
129 "sending scsi_close reply");
130 }
131
132
133 /*
134 * ndmpd_scsi_get_state_v2
135 *
136 * This handler returns state information for the currently open SCSI device.
137 * Since the implementation only supports the opening of a specific SCSI
138 * device, as opposed to a device that can talk to multiple SCSI targets,
139 * this request is not supported. This request is only appropriate for
140 * implementations that support device files that can target multiple
141 * SCSI devices.
142 *
143 * Parameters:
144 * connection (input) - connection handle.
145 * body (input) - request message body.
146 *
147 * Returns:
148 * void
149 */
150 /*ARGSUSED*/
151 void
152 ndmpd_scsi_get_state_v2(ndmp_connection_t *connection, void *body)
153 {
154 ndmp_scsi_get_state_reply reply;
155 ndmpd_session_t *session = ndmp_get_client_data(connection);
156
157 if (session->ns_scsi.sd_is_open == -1)
158 reply.error = NDMP_DEV_NOT_OPEN_ERR;
159 else if (!session->ns_scsi.sd_valid_target_set) {
160 reply.error = NDMP_NO_ERR;
161 reply.target_controller = -1;
162 reply.target_id = -1;
163 reply.target_lun = -1;
164 } else {
165 reply.error = NDMP_NO_ERR;
166 reply.target_controller = 0;
167 reply.target_id = session->ns_scsi.sd_sid;
168 reply.target_lun = session->ns_scsi.sd_lun;
169 }
170
171 ndmp_send_reply(connection, (void *) &reply,
172 "sending scsi_get_state reply");
173 }
174
175
176 /*
177 * ndmpd_scsi_set_target_v2
178 *
179 * This handler sets the SCSI target of the SCSI device.
180 * It is only valid to use this request if the opened SCSI device
181 * is capable of talking to multiple SCSI targets.
182 * Since the implementation only supports the opening of a specific SCSI
183 * device, as opposed to a device that can talk to multiple SCSI targets,
184 * this request is not supported. This request is only appropriate for
185 * implementations that support device files that can target multiple
186 * SCSI devices.
187 *
188 * Parameters:
189 * connection (input) - connection handle.
190 * body (input) - request message body.
191 *
192 * Returns:
193 * void
194 */
195 void
196 ndmpd_scsi_set_target_v2(ndmp_connection_t *connection, void *body)
197 {
198 ndmp_scsi_set_target_request_v2 *request;
199
200 request = (ndmp_scsi_set_target_request_v2 *) body;
201
202 common_set_target(connection, request->device.name,
203 request->target_controller, request->target_id,
204 request->target_lun);
205 }
206
207
208 /*
209 * ndmpd_scsi_reset_device_v2
210 *
211 * This handler resets the currently targeted SCSI device.
212 *
213 * Parameters:
214 * connection (input) - connection handle.
215 * body (input) - request message body.
216 *
217 * Returns:
218 * void
219 */
220 /*ARGSUSED*/
221 void
222 ndmpd_scsi_reset_device_v2(ndmp_connection_t *connection, void *body)
223 {
224 ndmp_scsi_reset_device_reply reply;
225
226
227 ndmpd_session_t *session = ndmp_get_client_data(connection);
228 struct uscsi_cmd cmd;
229
230 if (session->ns_scsi.sd_devid == -1) {
231 syslog(LOG_ERR, "SCSI device is not open.");
232 reply.error = NDMP_DEV_NOT_OPEN_ERR;
233 } else {
234 reply.error = NDMP_NO_ERR;
235 (void) memset((void*)&cmd, 0, sizeof (cmd));
236 cmd.uscsi_flags |= USCSI_RESET;
237 if (ioctl(session->ns_scsi.sd_devid, USCSICMD, &cmd) < 0) {
238 syslog(LOG_ERR, "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
264 ndmpd_scsi_reset_bus_v2(ndmp_connection_t *connection, void *body)
265 {
266 ndmp_scsi_reset_bus_reply reply;
267
268 reply.error = NDMP_NOT_SUPPORTED_ERR;
269
270 ndmp_send_reply(connection, (void *) &reply,
271 "sending scsi_reset_bus reply");
272 }
273
274
275 /*
276 * ndmpd_scsi_execute_cdb_v2
277 *
278 * This handler sends the CDB to the currently targeted SCSI device.
279 *
280 * Parameters:
281 * connection (input) - connection handle.
282 * body (input) - request message body.
283 *
284 * Returns:
285 * void
286 */
287 void
288 ndmpd_scsi_execute_cdb_v2(ndmp_connection_t *connection, void *body)
289 {
290 ndmp_execute_cdb_request *request = (ndmp_execute_cdb_request *) body;
291 ndmp_execute_cdb_reply reply;
292 ndmpd_session_t *session = ndmp_get_client_data(connection);
293
294 if (session->ns_scsi.sd_is_open == -1 ||
295 !session->ns_scsi.sd_valid_target_set) {
296 (void) memset((void *) &reply, 0, sizeof (reply));
297
298 reply.error = NDMP_DEV_NOT_OPEN_ERR;
299 ndmp_send_reply(connection, (void *) &reply,
300 "sending scsi_execute_cdb reply");
301 } else {
302 ndmp_execute_cdb(session, session->ns_scsi.sd_adapter_name,
303 session->ns_scsi.sd_sid, session->ns_scsi.sd_lun, request);
304 }
305 }
306
307
308 /*
309 * ************************************************************************
310 * NDMP V3 HANDLERS
311 * ************************************************************************
312 */
313
314 /*
315 * ndmpd_scsi_open_v3
316 *
317 * This handler opens the specified SCSI device.
318 *
319 * Parameters:
320 * connection (input) - connection handle.
321 * body (input) - request message body.
322 *
323 * Returns:
324 * void
325 */
326 void
327 ndmpd_scsi_open_v3(ndmp_connection_t *connection, void *body)
328 {
329 ndmp_scsi_open_request_v3 *request = (ndmp_scsi_open_request_v3 *)body;
330
331 common_open(connection, request->device);
332 }
333
334
335 /*
336 * ndmpd_scsi_set_target_v3
337 *
338 * This handler sets the SCSI target of the SCSI device.
339 * It is only valid to use this request if the opened SCSI device
340 * is capable of talking to multiple SCSI targets.
341 *
342 * Parameters:
343 * connection (input) - connection handle.
344 * body (input) - request message body.
345 *
346 * Returns:
347 * void
348 */
349 void
350 ndmpd_scsi_set_target_v3(ndmp_connection_t *connection, void *body)
351 {
352 ndmp_scsi_set_target_request_v3 *request;
353
354 request = (ndmp_scsi_set_target_request_v3 *) body;
355
356 common_set_target(connection, request->device,
357 request->target_controller, request->target_id,
358 request->target_lun);
359 }
360
361
362 /*
363 * ************************************************************************
364 * NDMP V4 HANDLERS
365 * ************************************************************************
366 */
367
368 /*
369 * ************************************************************************
370 * LOCALS
371 * ************************************************************************
372 */
373
374
375 /*
376 * scsi_open_send_reply
377 *
378 * Send a reply for SCSI open command
379 *
380 * Parameters:
381 * connection (input) - connection handle.
382 * err (input) - ndmp error code
383 *
384 * Returns:
385 * void
386 */
387 static void
388 scsi_open_send_reply(ndmp_connection_t *connection, int err)
389 {
390 ndmp_scsi_open_reply reply;
391
392 reply.error = err;
393 ndmp_send_reply(connection, (void *) &reply, "sending scsi_open reply");
394 }
395
396
397 /*
398 * common_open
399 *
400 * Common SCSI open function for all NDMP versions
401 *
402 * Parameters:
403 * connection (input) - connection handle.
404 * devname (input) - device name to open.
405 *
406 * Returns:
407 * void
408 */
409 static void
410 common_open(ndmp_connection_t *connection, char *devname)
411 {
412 ndmpd_session_t *session = ndmp_get_client_data(connection);
413 char adptnm[SCSI_MAX_NAME];
414 int sid, lun;
415 int err;
416 scsi_adapter_t *sa;
417 int devid = -1;
418
419 err = NDMP_NO_ERR;
420
421 if (session->ns_tape.td_fd != -1 || session->ns_scsi.sd_is_open != -1) {
422 err = NDMP_DEVICE_OPENED_ERR;
423 } else if ((sa = scsi_get_adapter(0)) != NULL) {
424 (void) strlcpy(adptnm, devname, SCSI_MAX_NAME-2);
425 adptnm[SCSI_MAX_NAME-1] = '\0';
426 sid = lun = -1;
427
428 scsi_find_sid_lun(sa, devname, &sid, &lun);
429 if (ndmp_open_list_find(devname, sid, lun) == NULL &&
430 (devid = open(devname, O_RDWR | O_NDELAY)) < 0) {
431 syslog(LOG_ERR, "Failed to open device %s: %m.",
432 devname);
433 err = NDMP_NO_DEVICE_ERR;
434 }
435 } else {
436 syslog(LOG_ERR, "%s: No such SCSI adapter.", devname);
437 err = NDMP_NO_DEVICE_ERR;
438 }
439
440 if (err != NDMP_NO_ERR) {
441 scsi_open_send_reply(connection, err);
442 return;
443 }
444
445 switch (ndmp_open_list_add(connection, adptnm, sid, lun, devid)) {
446 case 0:
447 /* OK */
448 break;
449 case EBUSY:
450 err = NDMP_DEVICE_BUSY_ERR;
451 break;
452 case ENOMEM:
453 err = NDMP_NO_MEM_ERR;
454 break;
455 default:
456 err = NDMP_IO_ERR;
457 }
458 if (err != NDMP_NO_ERR) {
459 scsi_open_send_reply(connection, err);
460 return;
461 }
462
463 (void) strlcpy(session->ns_scsi.sd_adapter_name, adptnm, SCSI_MAX_NAME);
464 session->ns_scsi.sd_is_open = 1;
465 session->ns_scsi.sd_devid = devid;
466 if (sid != -1) {
467 session->ns_scsi.sd_sid = sid;
468 session->ns_scsi.sd_lun = lun;
469 session->ns_scsi.sd_valid_target_set = TRUE;
470 } else {
471 session->ns_scsi.sd_sid = session->ns_scsi.sd_lun = -1;
472 session->ns_scsi.sd_valid_target_set = FALSE;
473 }
474
475 scsi_open_send_reply(connection, err);
476 }
477
478
479 /*
480 * common_set_target
481 *
482 * Set the SCSI target (SCSI number, LUN number, controller number)
483 *
484 * Parameters:
485 * connection (input) - connection handle.
486 * device (input) - device name.
487 * controller (input) - controller number.
488 * sid (input) - SCSI target ID.
489 * lun (input) - LUN number.
490 *
491 * Returns:
492 * 0: on success
493 * -1: otherwise
494 */
495 /*ARGSUSED*/
496 static void
497 common_set_target(ndmp_connection_t *connection, char *device,
498 ushort_t controller, ushort_t sid, ushort_t lun)
499 {
500 ndmp_scsi_set_target_reply reply;
501 ndmpd_session_t *session = ndmp_get_client_data(connection);
502 int type;
503
504 reply.error = NDMP_NO_ERR;
505
506 if (session->ns_scsi.sd_is_open == -1) {
507 reply.error = NDMP_DEV_NOT_OPEN_ERR;
508 } else if (!scsi_dev_exists(session->ns_scsi.sd_adapter_name, sid,
509 lun)) {
510 syslog(LOG_ERR, "No such SCSI device: target %d lun %d.",
511 sid, lun);
512 reply.error = NDMP_NO_DEVICE_ERR;
513 } else {
514 type = scsi_get_devtype(session->ns_scsi.sd_adapter_name, sid,
515 lun);
516 if (type != DTYPE_SEQUENTIAL && type != DTYPE_CHANGER) {
517 syslog(LOG_ERR,
518 "Not a tape or robot device: target %d lun %d.",
519 sid, lun);
520 reply.error = NDMP_ILLEGAL_ARGS_ERR;
521 }
522 }
523
524 if (reply.error != NDMP_NO_ERR) {
525 ndmp_send_reply(connection, (void *) &reply,
526 "sending scsi_set_target reply");
527 return;
528 }
529
530 /*
531 * The open_list must be updated if the SID or LUN are going to be
532 * changed. Close uses the same SID & LUN for removing the entry
533 * from the open_list.
534 */
535 if (sid != session->ns_scsi.sd_sid || lun != session->ns_scsi.sd_lun) {
536 switch (ndmp_open_list_add(connection,
537 session->ns_scsi.sd_adapter_name, sid, lun, 0)) {
538 case 0:
539 (void) ndmp_open_list_del(session->
540 ns_scsi.sd_adapter_name, session->ns_scsi.sd_sid,
541 session->ns_scsi.sd_lun);
542 break;
543 case EBUSY:
544 reply.error = NDMP_DEVICE_BUSY_ERR;
545 break;
546 case ENOMEM:
547 reply.error = NDMP_NO_MEM_ERR;
548 break;
549 default:
550 reply.error = NDMP_IO_ERR;
551 }
552 }
553
554 if (reply.error == NDMP_NO_ERR) {
555 session->ns_scsi.sd_sid = sid;
556 session->ns_scsi.sd_lun = lun;
557 session->ns_scsi.sd_valid_target_set = TRUE;
558 }
559
560 ndmp_send_reply(connection, (void *) &reply,
561 "sending scsi_set_target reply");
562 }
563
564 /*
565 * scsi_find_sid_lun
566 *
567 * gets the adapter, and returns the sid and lun number
568 */
569 void
570 scsi_find_sid_lun(scsi_adapter_t *sa, char *devname, int *sid, int *lun)
571 {
572 scsi_link_t *sl;
573 char *name;
574
575 for (sl = sa->sa_link_head.sl_next; sl && sl != &sa->sa_link_head;
576 sl = sl->sl_next) {
577 name = sasd_slink_name(sl);
578 if (strcmp(devname, name) == 0) {
579 *sid = sl->sl_sid;
580 *lun = sl->sl_lun;
581 return;
582 }
583 }
584
585 *sid = -1;
586 *lun = -1;
587 }