Add support for multiple sessions per iface to iscsid
[open-iscsi.git] / usr / iscsiadm.c
blob45b4c006d690d48926dab9b30aadd9653b15a19a
1 /*
2 * iSCSI Administration Utility
4 * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
5 * Copyright (C) 2006 Mike Christie
6 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
7 * maintained by open-iscsi@googlegroups.com
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * See the file COPYING included with this distribution for more details.
22 #include <errno.h>
23 #include <getopt.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <sys/stat.h>
31 #include "initiator.h"
32 #include "discovery.h"
33 #include "log.h"
34 #include "mgmt_ipc.h"
35 #include "idbm.h"
36 #include "iscsi_util.h"
37 #include "transport.h"
38 #include "version.h"
39 #include "iscsi_sysfs.h"
40 #include "list.h"
41 #include "iscsi_settings.h"
42 #include "fw_context.h"
43 #include "iface.h"
44 #include "session_info.h"
45 #include "host.h"
46 #include "sysdeps.h"
47 #include "idbm_fields.h"
48 #include "session_mgmt.h"
49 #include "iscsid_req.h"
50 #include "isns-proto.h"
51 #include "iscsi_err.h"
53 static char program_name[] = "iscsiadm";
54 static char config_file[TARGET_NAME_MAXLEN];
55 extern struct iscsi_ipc *ipc;
57 enum iscsiadm_mode {
58 MODE_DISCOVERY,
59 MODE_DISCOVERYDB,
60 MODE_NODE,
61 MODE_SESSION,
62 MODE_HOST,
63 MODE_IFACE,
64 MODE_FW,
67 enum iscsiadm_op {
68 OP_NOOP = 0x0,
69 OP_NEW = 0x1,
70 OP_DELETE = 0x2,
71 OP_UPDATE = 0x4,
72 OP_SHOW = 0x8,
73 OP_NONPERSISTENT = 0x10
76 static struct option const long_options[] =
78 {"mode", required_argument, NULL, 'm'},
79 {"portal", required_argument, NULL, 'p'},
80 {"targetname", required_argument, NULL, 'T'},
81 {"interface", required_argument, NULL, 'I'},
82 {"op", required_argument, NULL, 'o'},
83 {"type", required_argument, NULL, 't'},
84 {"name", required_argument, NULL, 'n'},
85 {"value", required_argument, NULL, 'v'},
86 {"host", required_argument, NULL, 'H'},
87 {"sid", required_argument, NULL, 'r'},
88 {"rescan", no_argument, NULL, 'R'},
89 {"print", required_argument, NULL, 'P'},
90 {"discover", no_argument, NULL, 'D'},
91 {"login", no_argument, NULL, 'l'},
92 {"loginall", required_argument, NULL, 'L'},
93 {"logout", no_argument, NULL, 'u'},
94 {"logoutall", required_argument, NULL, 'U'},
95 {"stats", no_argument, NULL, 's'},
96 {"killiscsid", required_argument, NULL, 'k'},
97 {"debug", required_argument, NULL, 'd'},
98 {"show", no_argument, NULL, 'S'},
99 {"version", no_argument, NULL, 'V'},
100 {"help", no_argument, NULL, 'h'},
101 {NULL, 0, NULL, 0},
103 static char *short_options = "RlDVhm:p:P:T:H:I:U:k:L:d:r:n:v:o:sSt:u";
105 static void usage(int status)
107 if (status != 0)
108 fprintf(stderr, "Try `%s --help' for more information.\n",
109 program_name);
110 else {
111 printf("\
112 iscsiadm -m discoverydb [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -Dl ] ] | [ [ -p ip:port -t type] \
113 [ -o operation ] [ -n name ] [ -v value ] [ -lD ] ] \n\
114 iscsiadm -m discovery [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -l ] ] | [ [ -p ip:port ] [ -l | -D ] ] \n\
115 iiscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] \
116 [ [ -o operation ] [ -n name ] [ -v value ] ]\n\
117 iscsiadm -m session [ -hV ] [ -d debug_level ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n\
118 iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ]\n\
119 iscsiadm -m fw [ -l ]\n\
120 iscsiadm -m host [ -P printlevel ] [ -H hostno ]\n\
121 iscsiadm -k priority\n");
123 exit(status);
126 static int
127 str_to_op(char *str)
129 int op;
131 if (!strcmp("new", str))
132 op = OP_NEW;
133 else if (!strcmp("delete", str))
134 op = OP_DELETE;
135 else if (!strcmp("update", str))
136 op = OP_UPDATE;
137 else if (!strcmp("show", str))
138 op = OP_SHOW;
139 else if (!strcmp("nonpersistent", str))
140 op = OP_NONPERSISTENT;
141 else
142 op = OP_NOOP;
144 return op;
147 static int
148 str_to_mode(char *str)
150 int mode;
152 if (!strcmp("discovery", str))
153 mode = MODE_DISCOVERY;
154 else if (!strcmp("discoverydb", str))
155 mode = MODE_DISCOVERYDB;
156 else if (!strcmp("node", str))
157 mode = MODE_NODE;
158 else if (!strcmp("session", str))
159 mode = MODE_SESSION;
160 else if (!strcmp("iface", str))
161 mode = MODE_IFACE;
162 else if (!strcmp("fw", str))
163 mode = MODE_FW;
164 else if (!strcmp("host", str))
165 mode = MODE_HOST;
166 else
167 mode = -1;
169 return mode;
172 static int
173 str_to_type(char *str)
175 int type;
177 if (!strcmp("sendtargets", str) ||
178 !strcmp("st", str))
179 type = DISCOVERY_TYPE_SENDTARGETS;
180 else if (!strcmp("slp", str))
181 type = DISCOVERY_TYPE_SLP;
182 else if (!strcmp("isns", str))
183 type = DISCOVERY_TYPE_ISNS;
184 else if (!strcmp("fw", str))
185 type = DISCOVERY_TYPE_FW;
186 else
187 type = -1;
189 return type;
192 static void kill_iscsid(int priority)
194 iscsiadm_req_t req;
195 iscsiadm_rsp_t rsp;
196 int rc;
199 * We only support SIGTERM like stoppage of iscsid for now.
200 * In the future we can do something where we try go finish
201 * up operations like login, error handling, etc, before
202 * iscsid is stopped, and we can add different values to indicate
203 * that the user wants iscsid to log out existing sessions before
204 * exiting.
206 if (priority != 0) {
207 log_error("Invalid iscsid priority %d. Priority must be 0.",
208 priority);
209 return;
212 memset(&req, 0, sizeof(req));
213 req.command = MGMT_IPC_IMMEDIATE_STOP;
214 rc = iscsid_exec_req(&req, &rsp, 0);
215 if (rc) {
216 iscsi_err_print_msg(rc);
217 log_error("Could not stop iscsid. Trying sending iscsid "
218 "SIGTERM or SIGKILL signals manually\n");
223 * TODO: we can display how the ifaces are related to node records.
224 * And we can add a scsi_host mode which would display how
225 * sessions are related to hosts
226 * (scsi_host and iscsi_sessions are the currently running instance of
227 * a iface or node record).
229 static int print_ifaces(struct iface_rec *iface, int info_level)
231 int err, num_found = 0;
233 switch (info_level) {
234 case 0:
235 case -1:
236 err = iface_for_each_iface(NULL, 0, &num_found,
237 iface_print_flat);
238 break;
239 case 1:
240 if (iface) {
241 err = iface_conf_read(iface);
242 if (err) {
243 log_error("Could not read iface %s.\n",
244 iface->name);
245 return err;
247 iface_print_tree(NULL, iface);
248 num_found = 1;
249 } else
250 err = iface_for_each_iface(NULL, 0, &num_found,
251 iface_print_tree);
252 break;
253 default:
254 log_error("Invalid info level %d. Try 0 - 1.", info_level);
255 return ISCSI_ERR_INVAL;
258 if (!num_found) {
259 log_error("No interfaces found.");
260 err = ISCSI_ERR_NO_OBJS_FOUND;
262 return err;
265 static int
266 match_startup_mode(node_rec_t *rec, char *mode)
268 if ((!strcmp(mode, "automatic") &&
269 rec->startup == ISCSI_STARTUP_AUTOMATIC) ||
270 (!strcmp(mode, "onboot") &&
271 rec->startup == ISCSI_STARTUP_ONBOOT) ||
272 (!strcmp(mode, "manual") &&
273 rec->startup == ISCSI_STARTUP_MANUAL) ||
274 !strcmp(mode, "all"))
275 return 0;
277 /* support conn or session startup params */
278 if ((!strcmp(mode, "automatic") &&
279 rec->conn[0].startup == ISCSI_STARTUP_AUTOMATIC) ||
280 (!strcmp(mode, "onboot") &&
281 rec->conn[0].startup == ISCSI_STARTUP_ONBOOT) ||
282 (!strcmp(mode, "manual") &&
283 rec->conn[0].startup == ISCSI_STARTUP_MANUAL) ||
284 !strcmp(mode, "all"))
285 return 0;
287 return -1;
290 static int
291 for_each_session(struct node_rec *rec, iscsi_sysfs_session_op_fn *fn)
293 int err, num_found = 0;
295 if (rec && rec->session.info) {
296 num_found = 1;
297 err = fn(rec, rec->session.info);
298 } else {
299 err = iscsi_sysfs_for_each_session(rec, &num_found, fn);
301 if (err)
302 log_error("Could not execute operation on all sessions: %s",
303 iscsi_err_to_str(err));
304 else if (!num_found) {
305 log_error("No session found.");
306 err = ISCSI_ERR_NO_OBJS_FOUND;
309 return err;
312 static int link_recs(void *data, struct node_rec *rec)
314 struct list_head *list = data;
315 struct node_rec *rec_copy;
317 rec_copy = calloc(1, sizeof(*rec_copy));
318 if (!rec_copy)
319 return ISCSI_ERR_NOMEM;
320 memcpy(rec_copy, rec, sizeof(*rec_copy));
321 INIT_LIST_HEAD(&rec_copy->list);
322 list_add_tail(&rec_copy->list, list);
323 return 0;
326 static int
327 __logout_by_startup(void *data, struct list_head *list,
328 struct session_info *info)
330 char *mode = data;
331 node_rec_t rec;
333 memset(&rec, 0, sizeof(node_rec_t));
334 if (idbm_rec_read(&rec, info->targetname, info->tpgt,
335 info->persistent_address,
336 info->persistent_port, &info->iface)) {
338 * this is due to a HW driver or some other driver
339 * not hooked in
341 log_debug(7, "could not read data for [%s,%s.%d]\n",
342 info->targetname, info->persistent_address,
343 info->persistent_port);
344 return -1;
347 /* multiple drivers could be connected to the same portal */
348 if (strcmp(rec.iface.transport_name, info->iface.transport_name))
349 return -1;
351 * we always skip on boot because if the user killed this on
352 * they would not be able to do anything
354 if (rec.startup == ISCSI_STARTUP_ONBOOT)
355 return -1;
357 if (match_startup_mode(&rec, mode))
358 return -1;
360 return iscsi_logout_portal(info, list);
363 static int
364 logout_by_startup(char *mode)
366 int nr_found;
367 int rc;
369 if (!mode || !(!strcmp(mode, "automatic") || !strcmp(mode, "all") ||
370 !strcmp(mode,"manual"))) {
371 log_error("Invalid logoutall option %s.", mode);
372 usage(ISCSI_ERR_INVAL);
373 return ISCSI_ERR_INVAL;
376 rc = iscsi_logout_portals(mode, &nr_found, 1, __logout_by_startup);
377 if (rc == ISCSI_ERR_NO_OBJS_FOUND)
378 log_error("No matching sessions found");
379 return rc;
383 * TODO: merged this and logout into the common for_each_matched_rec by making
384 * the matching more generic
386 static int
387 __login_by_startup(void *data, struct list_head *list, struct node_rec *rec)
389 char *mode = data;
391 if (match_startup_mode(rec, mode))
392 return -1;
394 return iscsi_login_portal(NULL, list, rec);
397 static int
398 login_by_startup(char *mode)
400 int nr_found = 0, err, rc;
401 struct list_head rec_list;
403 if (!mode || !(!strcmp(mode, "automatic") || !strcmp(mode, "all") ||
404 !strcmp(mode,"manual") || !strcmp(mode, "onboot"))) {
405 log_error("Invalid loginall option %s.", mode);
406 usage(ISCSI_ERR_INVAL);
407 return ISCSI_ERR_INVAL;
410 INIT_LIST_HEAD(&rec_list);
411 err = idbm_for_each_rec(&nr_found, &rec_list, link_recs);
412 if (err && !list_empty(&rec_list))
413 /* log msg and try to log into what we found */
414 log_error("Could not read all records: %s",
415 iscsi_err_to_str(err));
416 else if (err && list_empty(&rec_list)) {
417 log_error("Could not read node DB: %s.",
418 iscsi_err_to_str(err));
419 return err;
420 } else if (list_empty(&rec_list)) {
421 log_error("No records found");
422 return ISCSI_ERR_NO_OBJS_FOUND;
424 rc = err;
426 err = iscsi_login_portals(mode, &nr_found, 1, &rec_list,
427 __login_by_startup);
428 if (err)
429 log_error("Could not log into all portals");
431 if (err && !rc)
432 rc = err;
433 return rc;
437 * iscsi_logout_matched_portal - logout of targets matching the rec info
438 * @data: record to session with
439 * @list: list to add logout rec to
440 * @info: session to match with rec
442 static int iscsi_logout_matched_portal(void *data, struct list_head *list,
443 struct session_info *info)
445 struct node_rec *pattern_rec = data;
446 struct iscsi_transport *t;
448 t = iscsi_sysfs_get_transport_by_sid(info->sid);
449 if (!t)
450 return -1;
452 if (!iscsi_match_session(pattern_rec, info))
453 return -1;
455 /* we do not support this yet */
456 if (t->caps & CAP_FW_DB) {
457 log_error("Could not logout session of [sid: %d, "
458 "target: %s, portal: %s,%d].", info->sid,
459 info->targetname, info->persistent_address,
460 info->port);
461 log_error("Logout not supported for driver: %s.", t->name);
462 return -1;
464 return iscsi_logout_portal(info, list);
467 static int rec_match_fn(void *data, node_rec_t *rec)
469 struct rec_op_data *op_data = data;
471 if (!__iscsi_match_session(op_data->match_rec, rec->name,
472 rec->conn[0].address, rec->conn[0].port,
473 &rec->iface, rec->session.sid))
474 return -1;
475 return op_data->fn(op_data->data, rec);
478 static int __for_each_matched_rec(int verbose, struct node_rec *rec,
479 void *data, idbm_iface_op_fn *fn)
481 struct rec_op_data op_data;
482 int nr_found = 0, rc;
484 memset(&op_data, 0, sizeof(struct rec_op_data));
485 op_data.data = data;
486 op_data.match_rec = rec;
487 op_data.fn = fn;
489 rc = idbm_for_each_rec(&nr_found, &op_data, rec_match_fn);
490 if (rc) {
491 if (verbose)
492 log_error("Could not execute operation on all "
493 "records: %s", iscsi_err_to_str(rc));
494 } else if (!nr_found) {
495 if (verbose)
496 log_error("No records found");
497 rc = ISCSI_ERR_NO_OBJS_FOUND;
500 return rc;
503 static int for_each_matched_rec(struct node_rec *rec, void *data,
504 idbm_iface_op_fn *fn)
506 return __for_each_matched_rec(1, rec, data, fn);
510 static int login_portals(struct node_rec *pattern_rec)
512 struct list_head rec_list;
513 int nr_found, rc, err;
515 INIT_LIST_HEAD(&rec_list);
516 err = for_each_matched_rec(pattern_rec, &rec_list, link_recs);
517 if (err == ISCSI_ERR_NO_OBJS_FOUND)
518 return err;
519 else if (err && list_empty(&rec_list))
520 return err;
522 rc = err;
523 /* if there is an err but some recs then try to login to what we have */
525 err = iscsi_login_portals(pattern_rec, &nr_found, 1, &rec_list,
526 iscsi_login_portal);
527 if (err)
528 log_error("Could not log into all portals");
530 if (err && !rc)
531 rc = err;
533 return rc;
536 static int print_nodes(int info_level, struct node_rec *rec)
538 struct node_rec tmp_rec;
539 int rc = 0;
541 switch (info_level) {
542 case 0:
543 case -1:
544 rc = for_each_matched_rec(rec, NULL, idbm_print_node_flat);
545 break;
546 case 1:
547 memset(&tmp_rec, 0, sizeof(node_rec_t));
548 rc = for_each_matched_rec(rec, &tmp_rec,
549 idbm_print_node_and_iface_tree);
550 break;
551 default:
552 log_error("Invalid info level %d. Try 0 or 1.", info_level);
553 rc = ISCSI_ERR_INVAL;
556 return rc;
559 static char *get_config_file(void)
561 int rc;
562 iscsiadm_req_t req;
563 iscsiadm_rsp_t rsp;
565 memset(&req, 0, sizeof(req));
566 req.command = MGMT_IPC_CONFIG_FILE;
568 rc = iscsid_exec_req(&req, &rsp, 1);
569 if (rc)
570 return NULL;
572 if (rsp.u.config.var[0] != '\0') {
573 strcpy(config_file, rsp.u.config.var);
574 return config_file;
577 return NULL;
580 static int rescan_portal(void *data, struct session_info *info)
582 int host_no, err;
584 if (!iscsi_match_session(data, info))
585 return -1;
587 printf("Rescanning session [sid: %d, target: %s, portal: "
588 "%s,%d]\n", info->sid, info->targetname,
589 info->persistent_address, info->port);
591 host_no = iscsi_sysfs_get_host_no_from_sid(info->sid, &err);
592 if (err) {
593 log_error("Could not rescan session sid %d.", info->sid);
594 return err;
596 /* rescan each device to pick up size changes */
597 iscsi_sysfs_for_each_device(NULL, host_no, info->sid,
598 iscsi_sysfs_rescan_device);
599 /* now scan for new devices */
600 iscsi_sysfs_scan_host(host_no, 0);
601 return 0;
604 static int
605 session_stats(void *data, struct session_info *info)
607 int rc, i;
608 iscsiadm_req_t req;
609 iscsiadm_rsp_t rsp;
611 if (!iscsi_match_session(data, info))
612 return -1;
614 memset(&req, 0, sizeof(req));
615 req.command = MGMT_IPC_SESSION_STATS;
616 req.u.session.sid = info->sid;
618 rc = iscsid_exec_req(&req, &rsp, 1);
619 if (rc)
620 return rc;
622 printf("Stats for session [sid: %d, target: %s, portal: "
623 "%s,%d]\n",
624 info->sid, info->targetname, info->persistent_address,
625 info->port);
627 printf( "iSCSI SNMP:\n"
629 "\ttxdata_octets: %lld\n"
630 "\trxdata_octets: %lld\n"
632 "\tnoptx_pdus: %u\n"
633 "\tscsicmd_pdus: %u\n"
634 "\ttmfcmd_pdus: %u\n"
635 "\tlogin_pdus: %u\n"
636 "\ttext_pdus: %u\n"
637 "\tdataout_pdus: %u\n"
638 "\tlogout_pdus: %u\n"
639 "\tsnack_pdus: %u\n"
641 "\tnoprx_pdus: %u\n"
642 "\tscsirsp_pdus: %u\n"
643 "\ttmfrsp_pdus: %u\n"
644 "\ttextrsp_pdus: %u\n"
645 "\tdatain_pdus: %u\n"
646 "\tlogoutrsp_pdus: %u\n"
647 "\tr2t_pdus: %u\n"
648 "\tasync_pdus: %u\n"
649 "\trjt_pdus: %u\n"
651 "\tdigest_err: %u\n"
652 "\ttimeout_err: %u\n",
653 (unsigned long long)rsp.u.getstats.stats.txdata_octets,
654 (unsigned long long)rsp.u.getstats.stats.rxdata_octets,
656 rsp.u.getstats.stats.noptx_pdus,
657 rsp.u.getstats.stats.scsicmd_pdus,
658 rsp.u.getstats.stats.tmfcmd_pdus,
659 rsp.u.getstats.stats.login_pdus,
660 rsp.u.getstats.stats.text_pdus,
661 rsp.u.getstats.stats.dataout_pdus,
662 rsp.u.getstats.stats.logout_pdus,
663 rsp.u.getstats.stats.snack_pdus,
665 rsp.u.getstats.stats.noprx_pdus,
666 rsp.u.getstats.stats.scsirsp_pdus,
667 rsp.u.getstats.stats.tmfrsp_pdus,
668 rsp.u.getstats.stats.textrsp_pdus,
669 rsp.u.getstats.stats.datain_pdus,
670 rsp.u.getstats.stats.logoutrsp_pdus,
671 rsp.u.getstats.stats.r2t_pdus,
672 rsp.u.getstats.stats.async_pdus,
673 rsp.u.getstats.stats.rjt_pdus,
675 rsp.u.getstats.stats.digest_err,
676 rsp.u.getstats.stats.timeout_err);
678 if (rsp.u.getstats.stats.custom_length)
679 printf( "iSCSI Extended:\n");
681 for (i = 0; i < rsp.u.getstats.stats.custom_length; i++) {
682 printf("\t%s: %llu\n", rsp.u.getstats.stats.custom[i].desc,
683 (unsigned long long)rsp.u.getstats.stats.custom[i].value);
686 return 0;
689 static int add_static_rec(int *found, char *targetname, int tpgt,
690 char *ip, int port, struct iface_rec *iface)
692 node_rec_t *rec;
693 discovery_rec_t *drec;
694 int rc;
696 rec = calloc(1, sizeof(*rec));
697 if (!rec) {
698 log_error("Could not allocate memory for node addition");
699 rc = ISCSI_ERR_NOMEM;
700 goto done;
703 drec = calloc(1, sizeof(*drec));
704 if (!drec) {
705 log_error("Could not allocate memory for node addition");
706 rc = ISCSI_ERR_NOMEM;
707 goto free_rec;
709 drec->type = DISCOVERY_TYPE_STATIC;
711 idbm_node_setup_from_conf(rec);
712 strlcpy(rec->name, targetname, TARGET_NAME_MAXLEN);
713 rec->tpgt = tpgt;
714 rec->conn[0].port = port;
715 strlcpy(rec->conn[0].address, ip, NI_MAXHOST);
717 if (iface) {
718 rc = iface_conf_read(iface);
719 if (rc) {
720 log_error("Could not read iface %s. Error %d",
721 iface->name, rc);
722 return rc;
725 iface_copy(&rec->iface, iface);
728 rc = idbm_add_node(rec, drec, 1);
729 if (!rc) {
730 (*found)++;
731 printf("New iSCSI node [%s:" iface_fmt " %s,%d,%d %s] added\n",
732 rec->iface.transport_name, iface_str(&rec->iface),
733 ip, port, tpgt, targetname);
735 free(drec);
736 free_rec:
737 free(rec);
738 done:
739 return rc;
742 static int add_static_portal(int *found, void *data,
743 char *targetname, int tpgt, char *ip, int port)
745 node_rec_t *rec = data;
747 if (strlen(rec->conn[0].address) &&
748 strcmp(rec->conn[0].address, ip))
749 return -1;
751 if (rec->conn[0].port != -1 && rec->conn[0].port != port)
752 return -1;
754 return add_static_rec(found, targetname, tpgt, ip, port,
755 &rec->iface);
758 static int add_static_node(int *found, void *data,
759 char *targetname)
761 node_rec_t *rec = data;
763 if (!strlen(rec->name))
764 goto search;
766 if (strcmp(rec->name, targetname))
767 return -1;
769 if (!strlen(rec->conn[0].address))
770 goto search;
772 return add_static_rec(found, targetname, rec->tpgt,
773 rec->conn[0].address,
774 rec->conn[0].port, &rec->iface);
775 search:
776 return idbm_for_each_portal(found, data, add_static_portal,
777 targetname);
780 static int add_static_recs(struct node_rec *rec)
782 int rc, nr_found = 0;
784 rc = idbm_for_each_node(&nr_found, rec, add_static_node);
785 if (rc)
786 goto done;
787 /* success */
788 if (nr_found > 0)
789 return 0;
791 /* brand new target */
792 if (strlen(rec->name) && strlen(rec->conn[0].address)) {
793 rc = add_static_rec(&nr_found, rec->name, rec->tpgt,
794 rec->conn[0].address, rec->conn[0].port,
795 &rec->iface);
796 if (!rc)
797 return 0;
799 done:
800 log_error("Error while adding record: %s", iscsi_err_to_str(rc));
801 return rc;
805 * start sendtargets discovery process based on the
806 * particular config
808 static int
809 do_offload_sendtargets(discovery_rec_t *drec, int host_no, int do_login)
811 drec->type = DISCOVERY_TYPE_OFFLOAD_SENDTARGETS;
812 return discovery_offload_sendtargets(host_no, do_login, drec);
815 static int delete_node(void *data, struct node_rec *rec)
817 if (iscsi_check_for_running_session(rec)) {
819 * We could log out the session for the user, but if
820 * the session is being used the user may get something
821 * they were not expecting (FS errors and a read only
822 * remount).
824 log_error("This command will remove the record [iface: %s, "
825 "target: %s, portal: %s,%d], but a session is "
826 "using it. Logout session then rerun command to "
827 "remove record.", rec->iface.name, rec->name,
828 rec->conn[0].address, rec->conn[0].port);
829 return ISCSI_ERR_SESS_EXISTS;
832 return idbm_delete_node(rec);
835 static int delete_stale_rec(void *data, struct node_rec *rec)
837 struct list_head *new_rec_list = data;
838 struct node_rec *new_rec;
840 list_for_each_entry(new_rec, new_rec_list, list) {
842 * We could also move this to idbm.c and instead of looping
843 * over every node just loop over disc to node links.
845 if (rec->disc_type != new_rec->disc_type ||
846 rec->disc_port != new_rec->disc_port ||
847 strcmp(rec->disc_address, new_rec->disc_address))
849 * if we are not from the same discovery source
850 * ignore it
852 return -1;
854 if (__iscsi_match_session(rec,
855 new_rec->name,
856 new_rec->conn[0].address,
857 new_rec->conn[0].port,
858 &new_rec->iface,
859 new_rec->session.sid))
860 return -1;
862 /* if there is a error we can continue on */
863 return delete_node(NULL, rec);
866 static int
867 exec_disc_op_on_recs(discovery_rec_t *drec, struct list_head *rec_list,
868 int info_level, int do_login, int op)
870 int rc = 0, err, found = 0;
871 struct node_rec *new_rec, tmp_rec;
873 /* clean up node db */
874 if (op & OP_DELETE)
875 idbm_for_each_rec(&found, rec_list, delete_stale_rec);
877 if (op & OP_NEW || op & OP_UPDATE) {
878 /* now add/update records */
879 list_for_each_entry(new_rec, rec_list, list) {
880 rc = idbm_add_node(new_rec, drec, op & OP_UPDATE);
881 if (rc)
882 log_error("Could not add/update "
883 "[%s:" iface_fmt " %s,%d,%d %s]",
884 new_rec->iface.transport_name,
885 iface_str(&new_rec->iface),
886 new_rec->conn[0].address,
887 new_rec->conn[0].port,
888 new_rec->tpgt, new_rec->name);
892 memset(&tmp_rec, 0, sizeof(node_rec_t));
893 list_for_each_entry(new_rec, rec_list, list) {
894 switch (info_level) {
895 case 0:
896 case -1:
897 idbm_print_node_flat(NULL, new_rec);
898 break;
899 case 1:
900 idbm_print_node_and_iface_tree(&tmp_rec, new_rec);
905 if (!do_login)
906 return 0;
908 err = iscsi_login_portals(NULL, &found, 1, rec_list,
909 iscsi_login_portal);
910 if (err && !rc)
911 rc = err;
912 return rc;
915 static int
916 do_software_sendtargets(discovery_rec_t *drec, struct list_head *ifaces,
917 int info_level, int do_login, int op, int sync_drec)
919 struct list_head rec_list;
920 struct node_rec *rec, *tmp;
921 int rc;
923 INIT_LIST_HEAD(&rec_list);
925 * compat: if the user did not pass any op then we do all
926 * ops for them
928 if (!op)
929 op = OP_NEW | OP_DELETE | OP_UPDATE;
931 drec->type = DISCOVERY_TYPE_SENDTARGETS;
933 * we will probably want to know how a specific iface and discovery
934 * DB lined up, but for now just put all the targets found from
935 * a discovery portal in one place
937 if ((!(op & OP_NONPERSISTENT)) && sync_drec) {
938 rc = idbm_add_discovery(drec);
939 if (rc) {
940 log_error("Could not add new discovery record.");
941 return rc;
945 rc = idbm_bind_ifaces_to_nodes(discovery_sendtargets, drec, ifaces,
946 &rec_list);
947 if (rc) {
948 log_error("Could not perform SendTargets discovery: %s",
949 iscsi_err_to_str(rc));
950 return rc;
951 } else if (list_empty(&rec_list)) {
952 log_error("No portals found");
953 return ISCSI_ERR_NO_OBJS_FOUND;
956 rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op);
958 list_for_each_entry_safe(rec, tmp, &rec_list, list) {
959 list_del(&rec->list);
960 free(rec);
963 return rc;
966 static int
967 do_sendtargets(discovery_rec_t *drec, struct list_head *ifaces,
968 int info_level, int do_login, int op, int sync_drec)
970 struct iface_rec *tmp, *iface;
971 int rc, host_no;
972 struct iscsi_transport *t;
974 if (list_empty(ifaces)) {
975 ifaces = NULL;
976 goto sw_st;
979 /* we allow users to mix hw and sw iscsi so we have to sort it out */
980 list_for_each_entry_safe(iface, tmp, ifaces, list) {
981 rc = iface_conf_read(iface);
982 if (rc) {
983 log_error("Could not read iface info for %s. "
984 "Make sure a iface config with the file "
985 "name and iface.iscsi_ifacename %s is in %s.",
986 iface->name, iface->name, IFACE_CONFIG_DIR);
987 list_del(&iface->list);
988 free(iface);
989 continue;
992 host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc);
993 if (rc || host_no == -1) {
994 log_debug(1, "Could not match iface" iface_fmt " to "
995 "host.", iface_str(iface));
996 /* try software iscsi */
997 continue;
1000 t = iscsi_sysfs_get_transport_by_hba(host_no);
1001 if (!t) {
1002 log_error("Could not match hostno %d to "
1003 "transport. Dropping interface %s,"
1004 iface_fmt " ,%s.",
1005 host_no, iface->transport_name,
1006 iface_str(iface), iface->ipaddress);
1007 list_del(&iface->list);
1008 free(iface);
1009 continue;
1012 if (t->caps & CAP_SENDTARGETS_OFFLOAD) {
1013 do_offload_sendtargets(drec, host_no, do_login);
1014 list_del(&iface->list);
1015 free(iface);
1019 if (list_empty(ifaces))
1020 return ISCSI_ERR_NO_OBJS_FOUND;
1022 sw_st:
1023 return do_software_sendtargets(drec, ifaces, info_level, do_login,
1024 op, sync_drec);
1027 static int do_isns(discovery_rec_t *drec, struct list_head *ifaces,
1028 int info_level, int do_login, int op)
1030 struct list_head rec_list;
1031 struct node_rec *rec, *tmp;
1032 int rc;
1034 INIT_LIST_HEAD(&rec_list);
1036 * compat: if the user did not pass any op then we do all
1037 * ops for them
1039 if (!op)
1040 op = OP_NEW | OP_DELETE | OP_UPDATE;
1042 drec->type = DISCOVERY_TYPE_ISNS;
1044 rc = idbm_bind_ifaces_to_nodes(discovery_isns, drec, ifaces,
1045 &rec_list);
1046 if (rc) {
1047 log_error("Could not perform iSNS discovery: %s",
1048 iscsi_err_to_str(rc));
1049 return rc;
1050 } else if (list_empty(&rec_list)) {
1051 log_error("No portals found");
1052 return ISCSI_ERR_NO_OBJS_FOUND;
1055 rc = exec_disc_op_on_recs(drec, &rec_list, info_level, do_login, op);
1057 list_for_each_entry_safe(rec, tmp, &rec_list, list) {
1058 list_del(&rec->list);
1059 free(rec);
1062 return rc;
1065 static int
1066 verify_mode_params(int argc, char **argv, char *allowed, int skip_m)
1068 int ch, longindex;
1069 int ret = 0;
1071 optind = 0;
1073 while ((ch = getopt_long(argc, argv, short_options,
1074 long_options, &longindex)) >= 0) {
1075 if (!strchr(allowed, ch)) {
1076 if (ch == 'm' && skip_m)
1077 continue;
1078 ret = ch;
1079 break;
1083 return ret;
1086 static void catch_sigint( int signo ) {
1087 log_warning("caught SIGINT, exiting...");
1088 exit(1);
1091 /* TODO: merge iter helpers and clean them up, so we can use them here */
1092 static int exec_iface_op(int op, int do_show, int info_level,
1093 struct iface_rec *iface, char *name, char *value)
1095 struct db_set_param set_param;
1096 struct node_rec *rec = NULL;
1097 int rc = 0;
1099 switch (op) {
1100 case OP_NEW:
1101 if (!iface) {
1102 log_error("Could not add interface. No interface "
1103 "passed in.");
1104 return EINVAL;
1107 rec = idbm_create_rec(NULL, -1, NULL, -1, iface, 0);
1108 if (rec && iscsi_check_for_running_session(rec)) {
1109 rc = ISCSI_ERR_SESS_EXISTS;
1110 goto new_fail;
1113 iface_setup_defaults(iface);
1114 rc = iface_conf_write(iface);
1115 if (rc)
1116 goto new_fail;
1117 printf("New interface %s added\n", iface->name);
1118 break;
1119 new_fail:
1120 log_error("Could not create new interface %s.", iface->name);
1121 break;
1122 case OP_DELETE:
1123 if (!iface) {
1124 log_error("Could not delete interface. No interface "
1125 "passed in.");
1126 return ISCSI_ERR_INVAL;
1129 rec = idbm_create_rec(NULL, -1, NULL, -1, iface, 1);
1130 if (!rec) {
1131 rc = ISCSI_ERR_INVAL;
1132 goto delete_fail;
1135 /* logout and delete records using it first */
1136 rc = __for_each_matched_rec(0, rec, NULL, delete_node);
1137 if (rc && rc != ISCSI_ERR_NO_OBJS_FOUND)
1138 goto delete_fail;
1140 rc = iface_conf_delete(iface);
1141 if (rc)
1142 goto delete_fail;
1144 printf("%s unbound and deleted.\n", iface->name);
1145 break;
1146 delete_fail:
1147 log_error("Could not delete iface %s: %s", iface->name,
1148 iscsi_err_to_str(rc));
1149 break;
1150 case OP_UPDATE:
1151 if (!iface || !name || !value) {
1152 log_error("Update requires name, value, and iface.");
1153 rc = ISCSI_ERR_INVAL;
1154 break;
1157 rec = idbm_create_rec(NULL, -1, NULL, -1, iface, 1);
1158 if (!rec) {
1159 rc = ISCSI_ERR_INVAL;
1160 goto update_fail;
1163 if (iscsi_check_for_running_session(rec))
1164 log_warning("Updating iface while iscsi sessions "
1165 "are using it. You must logout the running "
1166 "sessions then log back in for the "
1167 "new settings to take affect.");
1169 if (!strcmp(name, IFACE_ISCSINAME)) {
1170 log_error("Can not update "
1171 "iface.iscsi_ifacename. Delete it, "
1172 "and then create a new one.");
1173 rc = ISCSI_ERR_INVAL;
1174 break;
1177 if (iface_is_bound_by_hwaddr(&rec->iface) &&
1178 !strcmp(name, IFACE_NETNAME)) {
1179 log_error("Can not update interface binding "
1180 "from hwaddress to net_ifacename. ");
1181 log_error("You must delete the interface and "
1182 "create a new one");
1183 rc = ISCSI_ERR_INVAL;
1184 break;
1187 if (iface_is_bound_by_netdev(&rec->iface) &&
1188 !strcmp(name, IFACE_HWADDR)) {
1189 log_error("Can not update interface binding "
1190 "from net_ifacename to hwaddress. ");
1191 log_error("You must delete the interface and "
1192 "create a new one");
1193 rc = ISCSI_ERR_INVAL;
1194 break;
1196 set_param.name = name;
1197 set_param.value = value;
1199 /* pass rec's iface because it has the db values */
1200 rc = iface_conf_update(&set_param, &rec->iface);
1201 if (rc)
1202 goto update_fail;
1204 rc = __for_each_matched_rec(0, rec, &set_param,
1205 idbm_node_set_param);
1206 if (rc == ISCSI_ERR_NO_OBJS_FOUND)
1207 rc = 0;
1208 else if (rc)
1209 goto update_fail;
1211 printf("%s updated.\n", iface->name);
1212 break;
1213 update_fail:
1214 log_error("Could not update iface %s: %s",
1215 iface->name, iscsi_err_to_str(rc));
1216 break;
1217 default:
1218 if (!iface || (iface && info_level > 0)) {
1219 if (op == OP_NOOP || op == OP_SHOW)
1220 rc = print_ifaces(iface, info_level);
1221 else
1222 rc = ISCSI_ERR_INVAL;
1223 } else {
1224 rc = iface_conf_read(iface);
1225 if (!rc)
1226 idbm_print_iface_info(&do_show, iface);
1227 else
1228 log_error("Could not read iface %s (%d).",
1229 iface->name, rc);
1233 if (rec)
1234 free(rec);
1235 return rc;
1238 /* TODO cleanup arguments */
1239 static int exec_node_op(int op, int do_login, int do_logout,
1240 int do_show, int do_rescan, int do_stats,
1241 int info_level, struct node_rec *rec,
1242 char *name, char *value)
1244 int rc = 0;
1245 struct db_set_param set_param;
1247 if (rec)
1248 log_debug(2, "%s: %s:%s node [%s,%s,%d] sid %u", __FUNCTION__,
1249 rec->iface.transport_name, rec->iface.name,
1250 rec->name, rec->conn[0].address, rec->conn[0].port,
1251 rec->session.sid);
1253 if (op == OP_NEW) {
1254 rc = add_static_recs(rec);
1255 goto out;
1258 if (do_rescan) {
1259 rc = for_each_session(rec, rescan_portal);
1260 goto out;
1263 if (do_stats) {
1264 rc = for_each_session(rec, session_stats);
1265 goto out;
1268 if (do_login && do_logout) {
1269 log_error("Invalid parameters. Both login and logout passed in");
1270 rc = ISCSI_ERR_INVAL;
1271 goto out;
1274 if ((do_login || do_logout) && op > OP_NOOP) {
1275 log_error("Invalid parameters. Login/logout and op passed in");
1276 rc = ISCSI_ERR_INVAL;
1277 goto out;
1280 if ((!do_login && !do_logout && op == OP_NOOP) &&
1281 (!strlen(rec->name) && !strlen(rec->conn[0].address) &&
1282 !strlen(rec->iface.name))) {
1283 rc = print_nodes(info_level, rec);
1284 goto out;
1287 if (do_login) {
1288 rc = login_portals(rec);
1289 goto out;
1292 if (do_logout) {
1293 int nr_found;
1295 rc = iscsi_logout_portals(rec, &nr_found, 1,
1296 iscsi_logout_matched_portal);
1297 if (rc == ISCSI_ERR_NO_OBJS_FOUND)
1298 log_error("No matching sessions found");
1299 goto out;
1302 if (op == OP_NOOP || (!do_login && !do_logout && op == OP_SHOW)) {
1303 rc = for_each_matched_rec(rec, &do_show, idbm_print_node_info);
1304 goto out;
1307 if (op == OP_UPDATE) {
1308 if (!name || !value) {
1309 log_error("update requires name and value");
1310 rc = ISCSI_ERR_INVAL;
1311 goto out;
1314 /* compat - old tools used node and iface transport name */
1315 if (!strncmp(name, "iface.", 6) &&
1316 strcmp(name, "iface.transport_name")) {
1317 log_error("Cannot modify %s. Use iface mode to update "
1318 "this value.", name);
1319 rc = ISCSI_ERR_INVAL;
1320 goto out;
1323 if (!strcmp(name, "node.transport_name"))
1324 name = "iface.transport_name";
1326 * tmp hack - we added compat crap above for the transport,
1327 * but want to fix Doran's issue in this release too. However
1328 * his patch is too harsh on many settings and we do not have
1329 * time to update apps so we have this tmp hack until we
1330 * can settle on a good interface that distros can use
1331 * and we can mark stable.
1333 if (!strcmp(name, "iface.transport_name")) {
1334 if (iscsi_check_for_running_session(rec)) {
1335 log_warning("Cannot modify node/iface "
1336 "transport name while a session "
1337 "is using it. Log out the session "
1338 "then update record.");
1339 rc = ISCSI_ERR_SESS_EXISTS;
1340 goto out;
1344 set_param.name = name;
1345 set_param.value = value;
1347 rc = for_each_matched_rec(rec, &set_param, idbm_node_set_param);
1348 goto out;
1349 } else if (op == OP_DELETE) {
1350 rc = for_each_matched_rec(rec, NULL, delete_node);
1351 goto out;
1352 } else {
1353 log_error("operation is not supported.");
1354 rc = ISCSI_ERR_INVAL;
1355 goto out;
1357 out:
1358 return rc;
1361 static int exec_fw_disc_op(discovery_rec_t *drec, struct list_head *ifaces,
1362 int info_level, int do_login, int op)
1364 struct list_head targets, rec_list, new_ifaces;
1365 struct iface_rec *iface, *tmp_iface;
1366 struct node_rec *rec, *tmp_rec;
1367 int rc = 0;
1369 INIT_LIST_HEAD(&targets);
1370 INIT_LIST_HEAD(&rec_list);
1371 INIT_LIST_HEAD(&new_ifaces);
1373 * compat: if the user did not pass any op then we do all
1374 * ops for them
1376 if (!op)
1377 op = OP_NEW | OP_DELETE | OP_UPDATE;
1380 * if a user passed in ifaces then we use them and ignore the ibft
1381 * net info
1383 if (!list_empty(ifaces)) {
1384 list_for_each_entry_safe(iface, tmp_iface, ifaces, list) {
1385 rc = iface_conf_read(iface);
1386 if (rc) {
1387 log_error("Could not read iface info for %s. "
1388 "Make sure a iface config with the "
1389 "file name and iface.iscsi_ifacename "
1390 "%s is in %s.", iface->name,
1391 iface->name, IFACE_CONFIG_DIR);
1392 list_del_init(&iface->list);
1393 free(iface);
1394 continue;
1397 goto discover_fw_tgts;
1401 * Next, check if we see any offload cards. If we do then
1402 * we make a iface if needed.
1404 * Note1: if there is not a offload card we do not setup
1405 * software iscsi binding with the nic used for booting,
1406 * because we do not know if that was intended.
1408 * Note2: we assume that the user probably wanted to access
1409 * all targets through all the ifaces instead of being limited
1410 * to what you can export in ibft.
1412 rc = fw_get_targets(&targets);
1413 if (rc) {
1414 log_error("Could not get list of targets from firmware. "
1415 "(err %d)\n", rc);
1416 return rc;
1418 rc = iface_create_ifaces_from_boot_contexts(&new_ifaces, &targets);
1419 if (rc)
1420 goto done;
1421 if (!list_empty(&new_ifaces))
1422 ifaces = &new_ifaces;
1424 discover_fw_tgts:
1425 rc = idbm_bind_ifaces_to_nodes(discovery_fw, drec,
1426 ifaces, &rec_list);
1427 if (rc)
1428 log_error("Could not perform fw discovery.\n");
1429 else
1430 rc = exec_disc_op_on_recs(drec, &rec_list, info_level,
1431 do_login, op);
1433 done:
1434 fw_free_targets(&targets);
1436 list_for_each_entry_safe(iface, tmp_iface, &new_ifaces, list) {
1437 list_del(&iface->list);
1438 free(iface);
1441 list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) {
1442 list_del(&rec->list);
1443 free(rec);
1445 return rc;
1448 static int exec_fw_op(discovery_rec_t *drec, struct list_head *ifaces,
1449 int info_level, int do_login, int op)
1451 struct boot_context *context;
1452 struct list_head targets, rec_list;
1453 struct node_rec *rec;
1454 int rc = 0;
1456 INIT_LIST_HEAD(&targets);
1457 INIT_LIST_HEAD(&rec_list);
1459 if (drec)
1460 return exec_fw_disc_op(drec, ifaces, info_level, do_login, op);
1462 /* The following ops do not interact with the DB */
1463 rc = fw_get_targets(&targets);
1464 if (rc) {
1465 log_error("Could not get list of targets from firmware. "
1466 "(err %d)\n", rc);
1467 return rc;
1470 if (do_login) {
1471 list_for_each_entry(context, &targets, list) {
1472 rec = idbm_create_rec_from_boot_context(context);
1473 if (!rec) {
1474 log_error("Could not convert firmware info to "
1475 "node record.\n");
1476 rc = ISCSI_ERR_NOMEM;
1477 break;
1480 iscsi_login_portal(NULL, NULL, rec);
1481 free(rec);
1483 } else {
1484 list_for_each_entry(context, &targets, list)
1485 fw_print_entry(context);
1488 fw_free_targets(&targets);
1489 return rc;
1492 static void setup_drec_defaults(int type, char *ip, int port,
1493 struct discovery_rec *drec)
1495 switch (type) {
1496 case DISCOVERY_TYPE_ISNS:
1497 idbm_isns_defaults(&drec->u.isns);
1498 break;
1499 case DISCOVERY_TYPE_SENDTARGETS:
1500 idbm_sendtargets_defaults(&drec->u.sendtargets);
1501 break;
1502 default:
1503 log_error("Invalid disc type.");
1505 strlcpy(drec->address, ip, sizeof(drec->address));
1506 drec->port = port;
1507 drec->type = type;
1511 * exec_discover - prep, add, read and exec discovery on drec
1512 * @type: discovery type
1513 * @ip: IP address
1514 * @port: port
1515 * @ifaces: list of ifaces to bind to
1516 * @info_level: print level
1517 * @do_login: set to 1 if discovery function should also log into portals found
1518 * @do_discover: set to 1 if discovery was requested
1519 * @op: ops passed in by user
1520 * @drec: discovery rec struct
1522 * This function determines what type of op needs to be executed
1523 * and will read and add a drec, and perform discovery if needed.
1525 * returns:
1526 * Greater than 0 - error
1527 * 0 - op/discovery completed
1528 * -1 - exec db op
1530 static int exec_discover(int disc_type, char *ip, int port,
1531 struct list_head *ifaces, int info_level,
1532 int do_login, int do_discover, int op,
1533 struct discovery_rec *drec)
1535 int rc;
1537 if (ip == NULL) {
1538 log_error("Please specify portal as <ipaddr>[:<ipport>]");
1539 return ISCSI_ERR_INVAL;
1542 if (op & OP_NEW && !do_discover) {
1543 setup_drec_defaults(disc_type, ip, port, drec);
1545 rc = idbm_add_discovery(drec);
1546 if (rc) {
1547 log_error("Could not add new discovery record.");
1548 return rc;
1549 } else {
1550 printf("New discovery record for [%s,%d] added.\n", ip,
1551 port);
1552 return 0;
1556 rc = idbm_discovery_read(drec, disc_type, ip, port);
1557 if (rc) {
1558 if (!do_discover) {
1559 log_error("Discovery record [%s,%d] not found.",
1560 ip, port);
1561 return rc;
1564 /* Just add default rec for user */
1565 log_debug(1, "Discovery record [%s,%d] not found!",
1566 ip, port);
1567 setup_drec_defaults(disc_type, ip, port, drec);
1568 if (!(op & OP_NONPERSISTENT)) {
1569 rc = idbm_add_discovery(drec);
1570 if (rc) {
1571 log_error("Could not add new discovery "
1572 "record.");
1573 return rc;
1576 } else if (!do_discover)
1577 return -1;
1579 rc = 0;
1580 switch (disc_type) {
1581 case DISCOVERY_TYPE_SENDTARGETS:
1583 * idbm_add_discovery call above handles drec syncing so
1584 * we always pass in 0 here.
1586 rc = do_sendtargets(drec, ifaces, info_level, do_login, op,
1588 break;
1589 case DISCOVERY_TYPE_ISNS:
1590 rc = do_isns(drec, ifaces, info_level, do_login, op);
1591 break;
1592 default:
1593 log_error("Unsupported discovery type.");
1594 break;
1597 return rc;
1600 static int exec_disc2_op(int disc_type, char *ip, int port,
1601 struct list_head *ifaces, int info_level, int do_login,
1602 int do_discover, int op, char *name, char *value,
1603 int do_show)
1605 struct discovery_rec drec;
1606 int rc = 0;
1608 memset(&drec, 0, sizeof(struct discovery_rec));
1609 if (disc_type != -1)
1610 drec.type = disc_type;
1612 switch (disc_type) {
1613 case DISCOVERY_TYPE_SENDTARGETS:
1614 if (port < 0)
1615 port = ISCSI_LISTEN_PORT;
1617 rc = exec_discover(disc_type, ip, port, ifaces, info_level,
1618 do_login, do_discover, op, &drec);
1619 if (rc < 0)
1620 goto do_db_op;
1621 goto done;
1622 case DISCOVERY_TYPE_SLP:
1623 log_error("SLP discovery is not fully implemented yet.");
1624 rc = ISCSI_ERR_INVAL;
1625 goto done;
1626 case DISCOVERY_TYPE_ISNS:
1627 if (port < 0)
1628 port = ISNS_DEFAULT_PORT;
1630 rc = exec_discover(disc_type, ip, port, ifaces, info_level,
1631 do_login, do_discover, op, &drec);
1632 if (rc < 0)
1633 goto do_db_op;
1634 goto done;
1635 case DISCOVERY_TYPE_FW:
1636 if (!do_discover) {
1637 log_error("Invalid command. Possibly missing "
1638 "--discover argument.");
1639 rc = ISCSI_ERR_INVAL;
1640 goto done;
1643 drec.type = DISCOVERY_TYPE_FW;
1644 rc = exec_fw_op(&drec, ifaces, info_level, do_login, op);
1645 goto done;
1646 default:
1647 rc = ISCSI_ERR_INVAL;
1649 if (!ip) {
1650 if (op == OP_NOOP || op == OP_SHOW) {
1651 if (idbm_print_all_discovery(info_level))
1652 /* successfully found some recs */
1653 rc = 0;
1654 else
1655 rc = ISCSI_ERR_NO_OBJS_FOUND;
1656 } else
1657 log_error("Invalid operation. Operation not "
1658 "supported.");
1659 } else if (op)
1660 log_error("Invalid command. Possibly missing discovery "
1661 "--type.");
1662 else
1663 log_error("Invalid command. Portal not needed or "
1664 "Possibly missing discovery --type.");
1665 goto done;
1668 do_db_op:
1669 rc = 0;
1671 if (op == OP_NOOP || op == OP_SHOW) {
1672 if (!idbm_print_discovery_info(&drec, do_show)) {
1673 log_error("No records found");
1674 rc = ISCSI_ERR_NO_OBJS_FOUND;
1676 } else if (op == OP_DELETE) {
1677 rc = idbm_delete_discovery(&drec);
1678 if (rc)
1679 log_error("Unable to delete record!");
1680 } else if (op == OP_UPDATE) {
1681 struct db_set_param set_param;
1683 if (!name || !value) {
1684 log_error("Update requires name and value.");
1685 rc = ISCSI_ERR_INVAL;
1686 goto done;
1688 set_param.name = name;
1689 set_param.value = value;
1690 rc = idbm_discovery_set_param(&set_param, &drec);
1691 } else {
1692 log_error("Operation is not supported.");
1693 rc = ISCSI_ERR_INVAL;
1694 goto done;
1696 done:
1697 return rc;
1700 static int exec_disc_op(int disc_type, char *ip, int port,
1701 struct list_head *ifaces, int info_level, int do_login,
1702 int do_discover, int op, char *name, char *value,
1703 int do_show)
1705 struct discovery_rec drec;
1706 int rc = 0;
1708 memset(&drec, 0, sizeof(struct discovery_rec));
1710 switch (disc_type) {
1711 case DISCOVERY_TYPE_SENDTARGETS:
1712 drec.type = DISCOVERY_TYPE_SENDTARGETS;
1714 if (port < 0)
1715 port = ISCSI_LISTEN_PORT;
1717 if (ip == NULL) {
1718 log_error("Please specify portal as "
1719 "<ipaddr>[:<ipport>]");
1720 rc = ISCSI_ERR_INVAL;
1721 goto done;
1724 idbm_sendtargets_defaults(&drec.u.sendtargets);
1725 strlcpy(drec.address, ip, sizeof(drec.address));
1726 drec.port = port;
1728 rc = do_sendtargets(&drec, ifaces, info_level,
1729 do_login, op, 1);
1730 if (rc)
1731 goto done;
1732 break;
1733 case DISCOVERY_TYPE_SLP:
1734 log_error("SLP discovery is not fully implemented yet.");
1735 rc = ISCSI_ERR_INVAL;
1736 break;
1737 case DISCOVERY_TYPE_ISNS:
1738 if (!ip) {
1739 log_error("Please specify portal as "
1740 "<ipaddr>:[<ipport>]");
1741 rc = ISCSI_ERR_INVAL;
1742 goto done;
1745 strlcpy(drec.address, ip, sizeof(drec.address));
1746 if (port < 0)
1747 drec.port = ISNS_DEFAULT_PORT;
1748 else
1749 drec.port = port;
1751 rc = do_isns(&drec, ifaces, info_level, do_login, op);
1752 if (rc)
1753 goto done;
1754 break;
1755 case DISCOVERY_TYPE_FW:
1756 drec.type = DISCOVERY_TYPE_FW;
1757 rc = exec_fw_op(&drec, ifaces, info_level, do_login, op);
1758 break;
1759 default:
1760 if (ip) {
1762 * We only have sendtargets disc recs in discovery
1763 * mode, so we can hardcode the port check to the
1764 * iscsi default here.
1766 * For isns or slp recs then discovery db mode
1767 * must be used.
1769 if (port < 0)
1770 port = ISCSI_LISTEN_PORT;
1772 if (idbm_discovery_read(&drec,
1773 DISCOVERY_TYPE_SENDTARGETS,
1774 ip, port)) {
1775 log_error("Discovery record [%s,%d] "
1776 "not found!", ip, port);
1777 rc = ISCSI_ERR_INVAL;
1778 goto done;
1780 if ((do_discover || do_login) &&
1781 drec.type == DISCOVERY_TYPE_SENDTARGETS) {
1782 rc = do_sendtargets(&drec, ifaces, info_level,
1783 do_login, op, 0);
1784 } else if (op == OP_NOOP || op == OP_SHOW) {
1785 if (!idbm_print_discovery_info(&drec,
1786 do_show)) {
1787 log_error("No records found");
1788 rc = ISCSI_ERR_NO_OBJS_FOUND;
1790 } else if (op == OP_DELETE) {
1791 rc = idbm_delete_discovery(&drec);
1792 if (rc)
1793 log_error("Unable to delete record!");
1794 } else if (op == OP_UPDATE || op == OP_NEW) {
1795 log_error("Operations new and update for "
1796 "discovery mode is not supported. "
1797 "Use discoverydb mode.");
1798 rc = ISCSI_ERR_INVAL;
1799 goto done;
1800 } else {
1801 log_error("Invalid operation.");
1802 rc = ISCSI_ERR_INVAL;
1803 goto done;
1805 } else if (op == OP_NOOP || op == OP_SHOW) {
1806 if (!idbm_print_all_discovery(info_level))
1807 rc = ISCSI_ERR_NO_OBJS_FOUND;
1808 goto done;
1809 } else {
1810 log_error("Invalid operation.");
1811 rc = ISCSI_ERR_INVAL;
1812 goto done;
1814 /* fall through */
1817 done:
1818 return rc;
1822 main(int argc, char **argv)
1824 char *ip = NULL, *name = NULL, *value = NULL;
1825 char *targetname = NULL, *group_session_mgmt_mode = NULL;
1826 int ch, longindex, mode=-1, port=-1, do_login=0, do_rescan=0;
1827 int rc=0, sid=-1, op=OP_NOOP, type=-1, do_logout=0, do_stats=0;
1828 int do_login_all=0, do_logout_all=0, info_level=-1, num_ifaces = 0;
1829 int tpgt = PORTAL_GROUP_TAG_UNKNOWN, killiscsid=-1, do_show=0;
1830 int do_discover = 0;
1831 struct sigaction sa_old;
1832 struct sigaction sa_new;
1833 struct list_head ifaces;
1834 struct iface_rec *iface = NULL, *tmp;
1835 struct node_rec *rec = NULL;
1836 uint32_t host_no = -1;
1838 INIT_LIST_HEAD(&ifaces);
1839 /* do not allow ctrl-c for now... */
1840 memset(&sa_old, 0, sizeof(struct sigaction));
1841 memset(&sa_new, 0, sizeof(struct sigaction));
1843 sa_new.sa_handler = catch_sigint;
1844 sigemptyset(&sa_new.sa_mask);
1845 sa_new.sa_flags = 0;
1846 sigaction(SIGINT, &sa_new, &sa_old );
1848 umask(0177);
1850 /* enable stdout logging */
1851 log_init(program_name, 1024, log_do_log_std, NULL);
1852 sysfs_init();
1854 optopt = 0;
1855 while ((ch = getopt_long(argc, argv, short_options,
1856 long_options, &longindex)) >= 0) {
1857 switch (ch) {
1858 case 'k':
1859 killiscsid = atoi(optarg);
1860 if (killiscsid < 0) {
1861 log_error("Invalid killiscsid priority %d "
1862 "Priority must be greater than or "
1863 "equal to zero.", killiscsid);
1864 rc = ISCSI_ERR_INVAL;
1865 goto free_ifaces;
1867 break;
1868 case 't':
1869 type = str_to_type(optarg);
1870 break;
1871 case 'o':
1872 op |= str_to_op(optarg);
1873 if (op == OP_NOOP) {
1874 log_error("can not recognize operation: '%s'",
1875 optarg);
1876 rc = ISCSI_ERR_INVAL;
1877 goto free_ifaces;
1879 break;
1880 case 'n':
1881 name = optarg;
1882 break;
1883 case 'v':
1884 value = optarg;
1885 break;
1886 case 'H':
1887 errno = 0;
1888 host_no = strtoul(optarg, NULL, 10);
1889 if (errno) {
1890 log_error("invalid host no %s. %s.",
1891 optarg, strerror(errno));
1892 rc = ISCSI_ERR_INVAL;
1893 goto free_ifaces;
1895 break;
1896 case 'r':
1897 sid = iscsi_sysfs_get_sid_from_path(optarg);
1898 if (sid < 0) {
1899 log_error("invalid sid '%s'",
1900 optarg);
1901 rc = ISCSI_ERR_INVAL;
1902 goto free_ifaces;
1904 break;
1905 case 'R':
1906 do_rescan = 1;
1907 break;
1908 case 'P':
1909 info_level = atoi(optarg);
1910 break;
1911 case 'D':
1912 do_discover = 1;
1913 break;
1914 case 'l':
1915 do_login = 1;
1916 break;
1917 case 'u':
1918 do_logout = 1;
1919 break;
1920 case 'U':
1921 do_logout_all = 1;
1922 group_session_mgmt_mode= optarg;
1923 break;
1924 case 'L':
1925 do_login_all= 1;
1926 group_session_mgmt_mode= optarg;
1927 break;
1928 case 's':
1929 do_stats = 1;
1930 break;
1931 case 'S':
1932 do_show = 1;
1933 break;
1934 case 'd':
1935 log_level = atoi(optarg);
1936 break;
1937 case 'm':
1938 mode = str_to_mode(optarg);
1939 break;
1940 case 'T':
1941 targetname = optarg;
1942 break;
1943 case 'p':
1944 ip = str_to_ipport(optarg, &port, &tpgt);
1945 break;
1946 case 'I':
1947 iface = iface_alloc(optarg, &rc);
1948 if (rc == ISCSI_ERR_INVAL) {
1949 printf("Invalid iface name %s. Must be from "
1950 "1 to %d characters.\n",
1951 optarg, ISCSI_MAX_IFACE_LEN - 1);
1952 goto free_ifaces;
1953 } else if (!iface || rc) {
1954 printf("Could not add iface %s.", optarg);
1955 rc = ISCSI_ERR_INVAL;
1956 goto free_ifaces;
1959 list_add_tail(&iface->list, &ifaces);
1960 num_ifaces++;
1961 break;
1962 case 'V':
1963 printf("%s version %s\n", program_name,
1964 ISCSI_VERSION_STR);
1965 return 0;
1966 case 'h':
1967 usage(0);
1971 if (optopt) {
1972 log_error("unrecognized character '%c'", optopt);
1973 rc = ISCSI_ERR_INVAL;
1974 goto free_ifaces;
1977 if (killiscsid >= 0) {
1978 kill_iscsid(killiscsid);
1979 goto free_ifaces;
1982 if (mode < 0)
1983 usage(ISCSI_ERR_INVAL);
1985 if (mode == MODE_FW) {
1986 if ((rc = verify_mode_params(argc, argv, "ml", 0))) {
1987 log_error("fw mode: option '-%c' is not "
1988 "allowed/supported", rc);
1989 rc = ISCSI_ERR_INVAL;
1990 goto free_ifaces;
1993 rc = exec_fw_op(NULL, NULL, info_level, do_login, op);
1994 goto free_ifaces;
1997 increase_max_files();
1998 if (idbm_init(get_config_file)) {
1999 log_warning("exiting due to idbm configuration error");
2000 rc = ISCSI_ERR_IDBM;
2001 goto free_ifaces;
2004 switch (mode) {
2005 case MODE_HOST:
2006 if ((rc = verify_mode_params(argc, argv, "HdmP", 0))) {
2007 log_error("host mode: option '-%c' is not "
2008 "allowed/supported", rc);
2009 rc = ISCSI_ERR_INVAL;
2010 goto out;
2013 rc = host_info_print(info_level, host_no);
2014 break;
2015 case MODE_IFACE:
2016 iface_setup_host_bindings();
2018 if ((rc = verify_mode_params(argc, argv, "IdnvmPo", 0))) {
2019 log_error("iface mode: option '-%c' is not "
2020 "allowed/supported", rc);
2021 rc = ISCSI_ERR_INVAL;
2022 goto out;
2025 if (!list_empty(&ifaces)) {
2026 iface = list_entry(ifaces.next, struct iface_rec,
2027 list);
2028 if (num_ifaces > 1)
2029 log_error("iface mode only accepts one "
2030 "interface. Using the first one "
2031 "%s.", iface->name);
2033 rc = exec_iface_op(op, do_show, info_level, iface,
2034 name, value);
2035 break;
2036 case MODE_DISCOVERYDB:
2037 if ((rc = verify_mode_params(argc, argv, "DSIPdmntplov", 0))) {
2038 log_error("discovery mode: option '-%c' is not "
2039 "allowed/supported", rc);
2040 rc = ISCSI_ERR_INVAL;
2041 goto out;
2044 rc = exec_disc2_op(type, ip, port, &ifaces, info_level,
2045 do_login, do_discover, op, name, value,
2046 do_show);
2047 break;
2048 case MODE_DISCOVERY:
2049 if ((rc = verify_mode_params(argc, argv, "DSIPdmntplov", 0))) {
2050 log_error("discovery mode: option '-%c' is not "
2051 "allowed/supported", rc);
2052 rc = ISCSI_ERR_INVAL;
2053 goto out;
2056 rc = exec_disc_op(type, ip, port, &ifaces, info_level,
2057 do_login, do_discover, op, name, value,
2058 do_show);
2059 break;
2060 case MODE_NODE:
2061 if ((rc = verify_mode_params(argc, argv, "RsPIdmlSonvupTUL",
2062 0))) {
2063 log_error("node mode: option '-%c' is not "
2064 "allowed/supported", rc);
2065 rc = ISCSI_ERR_INVAL;
2066 goto out;
2069 if (do_login_all) {
2070 rc = login_by_startup(group_session_mgmt_mode);
2071 goto out;
2074 if (do_logout_all) {
2075 rc = logout_by_startup(group_session_mgmt_mode);
2076 goto out;
2079 if (!list_empty(&ifaces)) {
2080 iface = list_entry(ifaces.next, struct iface_rec,
2081 list);
2082 if (num_ifaces > 1)
2083 log_error("NODE mode only accepts one "
2084 "interface. Using the first one "
2085 "driver %s hwaddress %s ipaddress "
2086 "%s.", iface->transport_name,
2087 iface->hwaddress, iface->ipaddress);
2090 if (ip && port == -1)
2091 port = ISCSI_LISTEN_PORT;
2093 rec = idbm_create_rec(targetname, tpgt, ip, port, iface, 1);
2094 if (!rec) {
2095 rc = ISCSI_ERR_NOMEM;
2096 goto out;
2099 rc = exec_node_op(op, do_login, do_logout, do_show,
2100 do_rescan, do_stats, info_level, rec,
2101 name, value);
2102 break;
2103 case MODE_SESSION:
2104 if ((rc = verify_mode_params(argc, argv,
2105 "PiRdrmusonuSv", 1))) {
2106 log_error("session mode: option '-%c' is not "
2107 "allowed or supported", rc);
2108 rc = ISCSI_ERR_INVAL;
2109 goto out;
2111 if (sid >= 0) {
2112 char session[64];
2113 struct session_info *info;
2115 snprintf(session, 63, "session%d", sid);
2116 session[63] = '\0';
2118 info = calloc(1, sizeof(*info));
2119 if (!info) {
2120 rc = ISCSI_ERR_NOMEM;
2121 goto out;
2124 rc = iscsi_sysfs_get_sessioninfo_by_id(info, session);
2125 if (rc) {
2126 log_error("Could not get session info for sid "
2127 "%d", sid);
2128 goto free_info;
2132 * We should be able to go on, but for now
2133 * we only support session mode ops if the module
2134 * is loaded and we support that module.
2136 if (!iscsi_sysfs_get_transport_by_sid(sid))
2137 goto free_info;
2139 if (!do_logout && !do_rescan && !do_stats &&
2140 op == OP_NOOP && info_level > 0) {
2141 rc = session_info_print(info_level, info);
2142 goto free_info;
2145 rec = idbm_create_rec(info->targetname,
2146 info->tpgt,
2147 info->persistent_address,
2148 info->persistent_port,
2149 &info->iface, 1);
2150 if (!rec) {
2151 rc = ISCSI_ERR_NOMEM;
2152 goto free_info;
2154 rec->session.info = info;
2155 rec->session.sid = sid;
2157 /* drop down to node ops */
2158 rc = exec_node_op(op, do_login, do_logout, do_show,
2159 do_rescan, do_stats, info_level,
2160 rec, name, value);
2161 free_info:
2162 free(info);
2163 goto out;
2164 } else {
2165 if (do_logout || do_rescan || do_stats) {
2166 rc = exec_node_op(op, do_login, do_logout,
2167 do_show, do_rescan, do_stats,
2168 info_level, NULL, name, value);
2169 goto out;
2172 rc = session_info_print(info_level, NULL);
2174 break;
2175 default:
2176 log_error("This mode is not yet supported");
2177 /* fall through */
2180 out:
2181 if (rec)
2182 free(rec);
2183 idbm_terminate();
2184 free_ifaces:
2185 list_for_each_entry_safe(iface, tmp, &ifaces, list) {
2186 list_del(&iface->list);
2187 free(iface);
2189 free_transports();
2190 sysfs_cleanup();
2191 return rc;