1 /*
2 Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #if defined(_WIN32)
22 #include <winsock2.h>
23 #include "win32_compat.h"
24 #pragma comment(lib, "ws2_32.lib")
25 WSADATA wsaData;
26 #endif
27
28 #ifdef HAVE_POLL_H
29 #include <poll.h>
30 #endif
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include "iscsi.h"
41 #include "scsi-lowlevel.h"
42
43 #ifndef discard_const
44 #define discard_const(ptr) ((void *)((intptr_t)(ptr)))
45 #endif
46
47 int showluns;
48 int useurls;
49 const char *initiator = "iqn.2007-10.com.github:sahlberg:libiscsi:iscsi-ls";
50
51 struct client_state {
52 int finished;
53 int status;
54 int lun;
55 int type;
56 const char *username;
57 const char *password;
58 };
59
60
event_loop(struct iscsi_context * iscsi,struct client_state * state)61 void event_loop(struct iscsi_context *iscsi, struct client_state *state)
62 {
63 struct pollfd pfd;
64
65 while (state->finished == 0) {
66 pfd.fd = iscsi_get_fd(iscsi);
67 pfd.events = iscsi_which_events(iscsi);
68
69 if (!pfd.events) {
70 sleep(1);
71 continue;
72 }
73
74 if (poll(&pfd, 1, -1) < 0) {
75 fprintf(stderr, "Poll failed");
76 exit(10);
77 }
78 if (iscsi_service(iscsi, pfd.revents) < 0) {
79 fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi));
80 exit(10);
81 }
82 }
83 }
84
show_lun(struct iscsi_context * iscsi,int lun)85 void show_lun(struct iscsi_context *iscsi, int lun)
86 {
87 struct scsi_task *task;
88 struct scsi_inquiry_standard *inq;
89 int type, no_media;
90 long long size = 0;
91 int size_pf = 0;
92 static const char sf[] = {' ', 'k', 'M', 'G', 'T' };
93
94 /* check we can talk to the lun */
95 tur_try_again:
96 if ((task = iscsi_testunitready_sync(iscsi, lun)) == NULL) {
97 fprintf(stderr, "testunitready failed\n");
98 exit(10);
99 }
100 if (task->status == SCSI_STATUS_CHECK_CONDITION) {
101 if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
102 scsi_free_scsi_task(task);
103 goto tur_try_again;
104 }
105 }
106
107 no_media = 0;
108 if (task->status == SCSI_STATUS_CHECK_CONDITION
109 && task->sense.key == SCSI_SENSE_NOT_READY
110 && task->sense.ascq == SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT) {
111 /* not an error, just a cdrom without a disk most likely */
112 no_media = 1;
113 } else if (task->status != SCSI_STATUS_GOOD) {
114 fprintf(stderr, "TESTUNITREADY failed with %s\n", iscsi_get_error(iscsi));
115 exit(10);
116 }
117 scsi_free_scsi_task(task);
118
119
120
121 /* check what type of lun we have */
122 task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 64);
123 if (task == NULL || task->status != SCSI_STATUS_GOOD) {
124 fprintf(stderr, "failed to send inquiry command : %s\n", iscsi_get_error(iscsi));
125 exit(10);
126 }
127 inq = scsi_datain_unmarshall(task);
128 if (inq == NULL) {
129 fprintf(stderr, "failed to unmarshall inquiry datain blob\n");
130 exit(10);
131 }
132 type = inq->device_type;
133 scsi_free_scsi_task(task);
134
135
136
137 if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {
138 struct scsi_readcapacity10 *rc10;
139
140 task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0);
141 if (task == NULL || task->status != SCSI_STATUS_GOOD) {
142 fprintf(stderr, "failed to send readcapacity command\n");
143 exit(10);
144 }
145
146 rc10 = scsi_datain_unmarshall(task);
147 if (rc10 == NULL) {
148 fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
149 exit(10);
150 }
151
152 size = rc10->block_size;
153 size *= rc10->lba;
154
155 for (size_pf=0; size_pf<4 && size > 1024; size_pf++) {
156 size /= 1024;
157 }
158
159 scsi_free_scsi_task(task);
160 }
161
162
163 printf("Lun:%-4d Type:%s", lun, scsi_devtype_to_str(type));
164 if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {
165 printf(" (Size:%lld%c)", size, sf[size_pf]);
166 }
167 if (no_media) {
168 printf(" (No media loaded)");
169 }
170 printf("\n");
171 }
172
list_luns(struct client_state * clnt,const char * target,const char * portal)173 void list_luns(struct client_state *clnt, const char *target, const char *portal)
174 {
175 struct iscsi_context *iscsi;
176 struct scsi_task *task;
177 struct scsi_reportluns_list *list;
178 int full_report_size;
179 int i;
180
181 if (strncasecmp(portal, "[fe80:", 6) == 0) {
182 fprintf(stderr, "skipping link-local address\n");
183 return;
184 }
185
186 iscsi = iscsi_create_context(initiator);
187 if (iscsi == NULL) {
188 printf("Failed to create context\n");
189 exit(10);
190 }
191
192 iscsi_set_initiator_username_pwd(iscsi, clnt->username, clnt->password);
193 if (iscsi_set_targetname(iscsi, target)) {
194 fprintf(stderr, "Failed to set target name\n");
195 exit(10);
196 }
197 iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
198 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
199
200 if (iscsi_full_connect_sync(iscsi, portal, -1) != 0) {
201 printf("list_luns: iscsi_connect failed. %s\n",
202 iscsi_get_error(iscsi));
203 exit(10);
204 }
205
206 /* get initial reportluns data, all targets can report 16 bytes but some
207 * fail if we ask for too much.
208 */
209 if ((task = iscsi_reportluns_sync(iscsi, 0, 16)) == NULL) {
210 fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi));
211 exit(10);
212 }
213 full_report_size = scsi_datain_getfullsize(task);
214 if (full_report_size > task->datain.size) {
215 scsi_free_scsi_task(task);
216
217 /* we need more data for the full list */
218 if ((task = iscsi_reportluns_sync(iscsi, 0, full_report_size)) == NULL) {
219 fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi));
220 exit(10);
221 }
222 }
223
224 list = scsi_datain_unmarshall(task);
225 if (list == NULL) {
226 fprintf(stderr, "failed to unmarshall reportluns datain blob\n");
227 exit(10);
228 }
229 for (i=0; i < (int)list->num; i++) {
230 show_lun(iscsi, list->luns[i]);
231 }
232
233 scsi_free_scsi_task(task);
234 iscsi_destroy_context(iscsi);
235 }
236
237
238
239
discoverylogout_cb(struct iscsi_context * iscsi,int status,void * command_data _U_,void * private_data)240 void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
241 {
242 struct client_state *state = (struct client_state *)private_data;
243
244 if (status != 0) {
245 fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi));
246 exit(10);
247 }
248
249 if (iscsi_disconnect(iscsi) != 0) {
250 fprintf(stderr, "Failed to disconnect old socket\n");
251 exit(10);
252 }
253
254 state->finished = 1;
255 }
256
discovery_cb(struct iscsi_context * iscsi,int status,void * command_data,void * private_data)257 void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
258 {
259 struct iscsi_discovery_address *addr;
260
261 if (status != 0) {
262 fprintf(stderr, "Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi));
263 exit(10);
264 }
265
266 for(addr=command_data; addr; addr=addr->next) {
267 struct iscsi_target_portal *portal = addr->portals;
268
269 while (portal != NULL) {
270 if (useurls == 1 && showluns == 0) {
271 char *str = strrchr(portal->portal, ',');
272 if (str != NULL) {
273 str[0] = 0;
274 }
275 printf("iscsi://%s/%s/0\n", portal->portal, addr->target_name);
276 } else {
277 printf("Target:%s Portal:%s\n", addr->target_name, portal->portal);
278 }
279 if (showluns != 0) {
280 list_luns(private_data, addr->target_name, portal->portal);
281 }
282 portal = portal->next;
283 }
284 }
285
286 if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) {
287 fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi));
288 exit(10);
289 }
290 }
291
292
discoverylogin_cb(struct iscsi_context * iscsi,int status,void * command_data _U_,void * private_data)293 void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
294 {
295 if (status != 0) {
296 fprintf(stderr, "Login failed. %s\n", iscsi_get_error(iscsi));
297 exit(10);
298 }
299
300 if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) {
301 fprintf(stderr, "failed to send discovery command : %s\n", iscsi_get_error(iscsi));
302 exit(10);
303 }
304 }
305
discoveryconnect_cb(struct iscsi_context * iscsi,int status,void * command_data _U_,void * private_data)306 void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
307 {
308 if (status != 0) {
309 fprintf(stderr, "discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi));
310 exit(10);
311 }
312
313 if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) {
314 fprintf(stderr, "iscsi_login_async failed : %s\n", iscsi_get_error(iscsi));
315 exit(10);
316 }
317 }
318
print_usage(void)319 void print_usage(void)
320 {
321 fprintf(stderr, "Usage: iscsi-ls [-?|--help] [-d|--debug] "
322 "[--usage] [-i|--initiator-name=iqn-name]\n"
323 "\t\t[-s|--show-luns] <iscsi-portal-url>\n");
324 }
325
print_help(void)326 void print_help(void)
327 {
328 fprintf(stderr, "Usage: iscsi-ls [OPTION...] <iscsi-url>\n");
329 fprintf(stderr, " -i, --initiator-name=iqn-name Initiatorname to use\n");
330 fprintf(stderr, " -d, --debug Print debug information\n");
331 fprintf(stderr, " -s, --show-luns Show the luns for each target\n");
332 fprintf(stderr, " --url Output targets in URL format\n");
333 fprintf(stderr, " (does not work with -s)\n");
334 fprintf(stderr, "\n");
335 fprintf(stderr, "Help options:\n");
336 fprintf(stderr, " -?, --help Show this help message\n");
337 fprintf(stderr, " --usage Display brief usage message\n");
338 fprintf(stderr, "\n");
339 fprintf(stderr, "iSCSI Portal URL format : %s\n", ISCSI_PORTAL_URL_SYNTAX);
340 fprintf(stderr, "\n");
341 fprintf(stderr, "<host> is either of:\n");
342 fprintf(stderr, " \"hostname\" iscsi.example\n");
343 fprintf(stderr, " \"ipv4-address\" 10.1.1.27\n");
344 fprintf(stderr, " \"ipv6-address\" [fce0::1]\n");
345 }
346
main(int argc,char * argv[])347 int main(int argc, char *argv[])
348 {
349 struct iscsi_context *iscsi;
350 struct iscsi_url *iscsi_url = NULL;
351 struct client_state state;
352 const char *url = NULL;
353 int i;
354 static int show_help = 0, show_usage = 0, debug = 0;
355
356 #ifdef _WIN32
357 if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
358 printf("Failed to start Winsock2\n");
359 exit(10);
360 }
361 #endif
362
363 for (i = 1; i < argc; i++) {
364 if (!strcmp(argv[i], "-?") ||
365 !strcmp(argv[i], "-h") ||
366 !strcmp(argv[i], "--help")) {
367 show_help = 1;
368 } else if (!strcmp(argv[i], "-u") ||
369 !strcmp(argv[i], "-usage")) {
370 show_usage = 1;
371 } else if (!strcmp(argv[i], "-d") ||
372 !strcmp(argv[i], "--debug")) {
373 debug = 1;
374 } else if (!strcmp(argv[i], "-i") ||
375 !strcmp(argv[i], "--initiator-name")) {
376 initiator = argv[++i];
377 } else if (!strcmp(argv[i], "-s") ||
378 !strcmp(argv[i], "--show-luns")) {
379 showluns = 1;
380 } else if (!strcmp(argv[i], "-U") ||
381 !strcmp(argv[i], "--url")) {
382 useurls = 1;
383 } else if (!strncmp("iscsi://", argv[i], 8)) {
384 url = strdup(argv[i]);
385 }
386 }
387
388 if (show_help != 0) {
389 print_help();
390 exit(0);
391 }
392
393 if (show_usage != 0) {
394 print_usage();
395 exit(0);
396 }
397
398 memset(&state, 0, sizeof(state));
399
400 if (url == NULL) {
401 fprintf(stderr, "You must specify iscsi target portal.\n");
402 print_usage();
403 exit(10);
404 }
405
406 iscsi = iscsi_create_context(initiator);
407 if (iscsi == NULL) {
408 printf("Failed to create context\n");
409 exit(10);
410 }
411
412 iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY);
413 iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
414
415 if (debug > 0) {
416 iscsi_set_log_level(iscsi, debug);
417 iscsi_set_log_fn(iscsi, iscsi_log_to_stderr);
418 }
419
420 iscsi_url = iscsi_parse_portal_url(iscsi, url);
421
422 if (url) {
423 free(discard_const(url));
424 }
425
426 if (iscsi_url == NULL) {
427 fprintf(stderr, "Failed to parse URL: %s\n",
428 iscsi_get_error(iscsi));
429 exit(10);
430 }
431
432 state.username = iscsi_url->user;
433 state.password = iscsi_url->passwd;
434
435 if (iscsi_connect_async(iscsi, iscsi_url->portal, discoveryconnect_cb, &state) != 0) {
436 fprintf(stderr, "connect_async: iscsi_connect failed. %s\n",
437 iscsi_get_error(iscsi));
438 exit(10);
439 }
440
441 event_loop(iscsi, &state);
442
443 iscsi_destroy_url(iscsi_url);
444 iscsi_destroy_context(iscsi);
445 return 0;
446 }
447
448