"Fossies" - the Fresh Open Source Software Archive

Member "open-iscsi-2.1.0/usr/iscsistart.c" (14 Nov 2019, 14633 Bytes) of package /linux/misc/open-iscsi-2.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "iscsistart.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.0.876_vs_2.0.877.

    1 /*
    2  * iSCSI Root Boot Program based on daemon code
    3  *
    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
    8  *
    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.
   13  *
   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.
   18  *
   19  * See the file COPYING included with this distribution for more details.
   20  */
   21 #include <ctype.h>
   22 #include <errno.h>
   23 #include <fcntl.h>
   24 #include <getopt.h>
   25 #include <stdlib.h>
   26 #include <stdio.h>
   27 #include <unistd.h>
   28 #include <string.h>
   29 #include <signal.h>
   30 #include <time.h>
   31 #include <sys/mman.h>
   32 #include <sys/utsname.h>
   33 #include <sys/signal.h>
   34 #include <sys/types.h>
   35 #include <sys/wait.h>
   36 
   37 #include "initiator.h"
   38 #include "iscsi_ipc.h"
   39 #include "event_poll.h"
   40 #include "transport.h"
   41 #include "log.h"
   42 #include "iscsi_util.h"
   43 #include "idbm.h"
   44 #include "idbm_fields.h"
   45 #include "version.h"
   46 #include "iscsi_sysfs.h"
   47 #include "iscsi_settings.h"
   48 #include "fw_context.h"
   49 #include "iface.h"
   50 #include "sysdeps.h"
   51 #include "iscsid_req.h"
   52 #include "iscsi_err.h"
   53 #include "iface.h"
   54 
   55 /* global config info */
   56 /* initiator needs initiator name/alias */
   57 struct iscsi_daemon_config daemon_config;
   58 struct iscsi_daemon_config *dconfig = &daemon_config;
   59 
   60 static node_rec_t config_rec;
   61 static LIST_HEAD(targets);
   62 static LIST_HEAD(user_params);
   63 
   64 static char program_name[] = "iscsistart";
   65 
   66 /* used by initiator */
   67 extern struct iscsi_ipc *ipc;
   68 
   69 static struct option const long_options[] = {
   70     {"initiatorname", required_argument, NULL, 'i'},
   71     {"targetname", required_argument, NULL, 't'},
   72     {"tgpt", required_argument, NULL, 'g'},
   73     {"address", required_argument, NULL, 'a'},
   74     {"port", required_argument, NULL, 'p'},
   75     {"username", required_argument, NULL, 'u'},
   76     {"password", required_argument, NULL, 'w'},
   77     {"username_in", required_argument, NULL, 'U'},
   78     {"password_in", required_argument, NULL, 'W'},
   79     {"debug", required_argument, NULL, 'd'},
   80     {"fwparam_connect", no_argument, NULL, 'b'},
   81     {"fwparam_network", no_argument, NULL, 'N'},
   82     {"fwparam_print", no_argument, NULL, 'f'},
   83     {"param", required_argument, NULL, 'P'},
   84     {"help", no_argument, NULL, 'h'},
   85     {"version", no_argument, NULL, 'v'},
   86     {NULL, 0, NULL, 0},
   87 };
   88 
   89 static void usage(int status)
   90 {
   91     if (status != 0)
   92         fprintf(stderr, "Try `%s --help' for more information.\n",
   93             program_name);
   94     else {
   95         printf("Usage: %s [OPTION]\n", program_name);
   96         printf("\
   97 Open-iSCSI initiator.\n\
   98   -i, --initiatorname=name set InitiatorName to name (Required)\n\
   99   -t, --targetname=name    set TargetName to name (Required)\n\
  100   -g, --tgpt=N             set target portal group tag to N (Required)\n\
  101   -a, --address=A.B.C.D    set IP address to A.B.C.D (Required)\n\
  102   -p, --port=N             set port to N (Default 3260)\n\
  103   -u, --username=N         set username to N (optional)\n\
  104   -w, --password=N         set password to N (optional\n\
  105   -U, --username_in=N      set incoming username to N (optional)\n\
  106   -W, --password_in=N      set incoming password to N (optional)\n\
  107   -d, --debug=debuglevel   print debugging information \n\
  108   -b, --fwparam_connect    create a session to the target using iBFT or OF\n\
  109   -N, --fwparam_network    bring up the network as specified by iBFT or OF\n\
  110   -f, --fwparam_print      print the iBFT or OF info to STDOUT \n\
  111   -P, --param=NAME=VALUE   set parameter with the name NAME to VALUE\n\
  112   -h, --help               display this help and exit\n\
  113   -v, --version            display version and exit\n\
  114 ");
  115     }
  116     exit(status);
  117 }
  118 
  119 static int stop_event_loop(void)
  120 {
  121     iscsiadm_req_t req;
  122     iscsiadm_rsp_t rsp;
  123     int rc;
  124 
  125     memset(&req, 0, sizeof(req));
  126     req.command = MGMT_IPC_IMMEDIATE_STOP;
  127     rc = iscsid_exec_req(&req, &rsp, 0, -1);
  128     if (rc) {
  129         iscsi_err_print_msg(rc);
  130         log_error("Could not stop event_loop");
  131     }
  132     return rc;
  133 }
  134 
  135 static int apply_params(struct node_rec *rec)
  136 {
  137     struct user_param *param;
  138     int rc;
  139 
  140     /* Must init this so we can check if user overrode them */
  141     rec->session.initial_login_retry_max = -1;
  142     rec->conn[0].timeo.noop_out_interval = -1;
  143     rec->conn[0].timeo.noop_out_timeout = -1;
  144     rec->session.scan = -1;
  145 
  146     list_for_each_entry(param, &user_params, list) {
  147         /*
  148          * user may not have passed in all params that were set by
  149          * ibft/iscsi_boot, so clear out values that might conflict
  150          * with user overrides
  151          */
  152         if (!strcmp(param->name, IFACE_NETNAME)) {
  153             /* overriding netname so MAC will be for old netdev */
  154             memset(rec->iface.hwaddress, 0,
  155                 sizeof(rec->iface.hwaddress));
  156         } else if (!strcmp(param->name, IFACE_HWADDR)) {
  157             /* overriding MAC so netdev will be for old MAC */
  158             memset(rec->iface.netdev, 0, sizeof(rec->iface.netdev));
  159         } else if (!strcmp(param->name, IFACE_TRANSPORTNAME)) {
  160             /*
  161              * switching drivers so all old binding info is no
  162              * longer valid. Old values were either for offload
  163              * and we are switching to software or the reverse,
  164              * or switching types of cards (bnx2i to cxgb3i).
  165              */
  166             memset(&rec->iface, 0, sizeof(rec->iface));
  167             iface_setup_defaults(&rec->iface);
  168         }
  169     }
  170 
  171     rc = idbm_node_set_rec_from_param(&user_params, rec, 0);
  172     if (rc)
  173         return rc;
  174 
  175     /*
  176      * For root boot we could not change this in older versions so
  177      * if user did not override then use the defaults.
  178      *
  179      * Increase to account for boot using static setup.
  180      */
  181     if (rec->session.initial_login_retry_max == -1)
  182         rec->session.initial_login_retry_max = 30;
  183     /* we used to not be able to answer so turn off */
  184     if (rec->conn[0].timeo.noop_out_interval == -1)
  185         rec->conn[0].timeo.noop_out_interval = 0;
  186     if (rec->conn[0].timeo.noop_out_timeout == -1)
  187         rec->conn[0].timeo.noop_out_timeout = 0;
  188     if (rec->session.scan == -1)
  189         rec->session.scan = DEF_INITIAL_SCAN;
  190 
  191     return 0;
  192 }
  193 
  194 static int parse_param(char *param_str)
  195 {
  196     struct user_param *param;
  197     char *name, *value;
  198 
  199     name = param_str;
  200 
  201     value = strchr(param_str, '=');
  202     if (!value) {
  203         log_error("Invalid --param %s. Missing value.", param_str);
  204         return ISCSI_ERR_INVAL;
  205     }
  206     *value = '\0';
  207 
  208     value++;
  209     if (!strlen(value)) {
  210         log_error("Invalid --param %s. Missing value.", param_str);
  211         return ISCSI_ERR_INVAL;
  212     }
  213 
  214     param = idbm_alloc_user_param(name, value);
  215     if (!param) {
  216         log_error("Could not allocate memory for param.");
  217         return ISCSI_ERR_NOMEM;
  218     }
  219 
  220     list_add(&param->list, &user_params);
  221     return 0;
  222 }
  223 
  224 static int login_session(struct node_rec *rec)
  225 {
  226     iscsiadm_req_t req;
  227     iscsiadm_rsp_t rsp;
  228     int rc, msec, err;
  229     struct timespec ts;
  230 
  231     rc = apply_params(rec);
  232     if (rc)
  233         return rc;
  234 
  235     printf("%s: Logging into %s %s:%d,%d\n", program_name, rec->name,
  236         rec->conn[0].address, rec->conn[0].port,
  237         rec->tpgt);
  238     memset(&req, 0, sizeof(req));
  239     req.command = MGMT_IPC_SESSION_LOGIN;
  240     memcpy(&req.u.session.rec, rec, sizeof(*rec));
  241 
  242     /*
  243      * Need to handle race where iscsid proc is starting up while we are
  244      * trying to connect. Retry with exponential backoff, start from 50 ms.
  245      */
  246     for (msec = 50; msec <= 15000; msec <<= 1) {
  247         rc = iscsid_exec_req(&req, &rsp, 0, ISCSID_REQ_TIMEOUT);
  248         if (rc == 0) {
  249             return rc;
  250         } else if (rc == ISCSI_ERR_ISCSID_NOTCONN) {
  251             ts.tv_sec = msec / 1000;
  252             ts.tv_nsec = (msec % 1000) * 1000000L;
  253 
  254             /* On EINTR, retry nanosleep with remaining time. */
  255             while ((err = nanosleep(&ts, &ts)) < 0 &&
  256                    errno == EINTR);
  257             if (err < 0)
  258                 break;
  259         } else {
  260             break;
  261         }
  262     }
  263 
  264     iscsi_err_print_msg(rc);
  265     return rc;
  266 }
  267 
  268 static int setup_session(void)
  269 {
  270     struct boot_context *context;
  271     int rc = 0, rc2 = 0;
  272 
  273     if (list_empty(&targets))
  274         return login_session(&config_rec);
  275 
  276     list_for_each_entry(context, &targets, list) {
  277         struct node_rec *rec;
  278 
  279         rec = idbm_create_rec_from_boot_context(context);
  280         if (!rec) {
  281             log_error("Could not allocate memory. Could "
  282                   "not start boot session to "
  283                   "%s,%s,%d", context->targetname,
  284                   context->target_ipaddr,
  285                   context->target_port);
  286             continue;
  287         }
  288 
  289         rc2 = login_session(rec);
  290         if (rc2)
  291             rc = rc2;
  292         free(rec);
  293     }
  294     fw_free_targets(&targets);
  295     return rc;
  296 }
  297 
  298 int session_in_use(int sid) { return 0; }
  299 
  300 static void catch_signal(int signo)
  301 {
  302     log_warning("pid %d caught signal -%d", getpid(), signo);
  303 }
  304 
  305 static int check_params(char *initiatorname)
  306 {
  307     if (!initiatorname) {
  308         log_error("InitiatorName not set. Exiting %s", program_name);
  309         return EINVAL;
  310     }
  311 
  312     if (config_rec.tpgt == PORTAL_GROUP_TAG_UNKNOWN) {
  313         log_error("Portal Group not set. Exiting %s", program_name);
  314         return EINVAL;
  315     }
  316 
  317     if (!strlen(config_rec.name)) {
  318         log_error("TargetName not set. Exiting %s", program_name);
  319         return EINVAL;
  320     }
  321 
  322     if (!strlen(config_rec.conn[0].address)) {
  323         log_error("IP Address not set. Exiting %s", program_name);
  324         return EINVAL;
  325     }
  326 
  327     return 0;
  328 }
  329 
  330 #define check_str_param_len(str, max_len, param)            \
  331 do {                                    \
  332     if (strlen(str) > max_len) {                    \
  333         printf("%s: invalid %s %s. Max %s length is %d.\n", \
  334             program_name, param, str, param, max_len);  \
  335         exit(ISCSI_ERR_INVAL);                  \
  336     }                               \
  337 } while (0);
  338 
  339 int main(int argc, char *argv[])
  340 {
  341     struct utsname host_info; /* will use to compound initiator alias */
  342     struct iscsi_auth_config *auth;
  343     char *initiatorname = NULL;
  344     int ch, longindex, ret;
  345     struct boot_context *context, boot_context;
  346     struct sigaction sa_old;
  347     struct sigaction sa_new;
  348     int control_fd, mgmt_ipc_fd, err;
  349     pid_t pid;
  350 
  351     idbm_node_setup_defaults(&config_rec);
  352     config_rec.name[0] = '\0';
  353     config_rec.conn[0].address[0] = '\0';
  354     auth = &config_rec.session.auth;
  355 
  356     /* do not allow ctrl-c for now... */
  357     sa_new.sa_handler = catch_signal;
  358     sigemptyset(&sa_new.sa_mask);
  359     sa_new.sa_flags = 0;
  360     sigaction(SIGINT, &sa_new, &sa_old );
  361 
  362     /* initialize logger */
  363     log_init(program_name, DEFAULT_AREA_SIZE, log_do_log_std, NULL);
  364 
  365     sysfs_init();
  366 
  367     while ((ch = getopt_long(argc, argv, "P:i:t:g:a:p:d:u:w:U:W:bNfvh",
  368                  long_options, &longindex)) >= 0) {
  369         switch (ch) {
  370         case 'i':
  371             initiatorname = optarg;
  372             break;
  373         case 't':
  374             check_str_param_len(optarg, TARGET_NAME_MAXLEN,
  375                         "targetname");
  376             strlcpy(config_rec.name, optarg, TARGET_NAME_MAXLEN);
  377             break;
  378         case 'g':
  379             config_rec.tpgt = atoi(optarg);
  380             break;
  381         case 'a':
  382             check_str_param_len(optarg, NI_MAXHOST, "address");
  383             strlcpy(config_rec.conn[0].address, optarg, NI_MAXHOST);
  384             break;
  385         case 'p':
  386             config_rec.conn[0].port = atoi(optarg);
  387             break;
  388         case 'w':
  389             check_str_param_len(optarg, AUTH_STR_MAX_LEN,
  390                        "password");
  391             strlcpy((char *)auth->password, optarg,
  392                 AUTH_STR_MAX_LEN);
  393             auth->password_length = strlen((char *)auth->password);
  394             break;
  395         case 'W':
  396             check_str_param_len(optarg, AUTH_STR_MAX_LEN,
  397                        "password_in");
  398             strlcpy((char *)auth->password_in, optarg,
  399                 AUTH_STR_MAX_LEN);
  400             auth->password_in_length =
  401                 strlen((char *)auth->password_in);
  402             break;
  403         case 'u':
  404             check_str_param_len(optarg, AUTH_STR_MAX_LEN,
  405                         "username");
  406             strlcpy(auth->username, optarg, AUTH_STR_MAX_LEN);
  407             break;
  408         case 'U':
  409             check_str_param_len(optarg, AUTH_STR_MAX_LEN,
  410                         "username_in");
  411             strlcpy(auth->username_in, optarg, AUTH_STR_MAX_LEN);
  412             break;
  413         case 'd':
  414             log_level = atoi(optarg);
  415             break;
  416         case 'b':
  417             memset(&boot_context, 0, sizeof(boot_context));
  418             ret = fw_get_entry(&boot_context);
  419             if (ret) {
  420                 printf("Could not get boot entry.\n");
  421                 exit(ret);
  422             }
  423 
  424             initiatorname = boot_context.initiatorname;
  425             ret = fw_get_targets(&targets);
  426             if (ret || list_empty(&targets)) {
  427                 printf("Could not setup fw entries.\n");
  428                 exit(ret);
  429             }
  430             break;
  431         case 'N':
  432             exit(fw_setup_nics());
  433         case 'f':
  434             ret = fw_get_targets(&targets);
  435             if (ret || list_empty(&targets)) {
  436                 printf("Could not get list of targets from "
  437                        "firmware.\n");
  438                 exit(ret);
  439             }
  440 
  441             list_for_each_entry(context, &targets, list)
  442                 fw_print_entry(context);
  443 
  444             fw_free_targets(&targets);
  445             exit(0);
  446         case 'P':
  447             err = parse_param(optarg);
  448             if (err)
  449                 exit(err);
  450             break;
  451         case 'v':
  452             printf("%s version %s\n", program_name,
  453                 ISCSI_VERSION_STR);
  454             exit(0);
  455         case 'h':
  456             usage(0);
  457             break;
  458         default:
  459             usage(ISCSI_ERR_INVAL);
  460             break;
  461         }
  462     }
  463 
  464     if (list_empty(&targets) && check_params(initiatorname))
  465         exit(ISCSI_ERR_INVAL);
  466 
  467     pid = fork();
  468     if (pid < 0) {
  469         log_error("iscsiboot fork failed");
  470         exit(ISCSI_ERR_NOMEM);
  471     } else if (pid) {
  472         int status, rc, rc2;
  473 
  474         /* make a special socket path for only this iscsistart instance */
  475         iscsid_set_namespace(pid);
  476         sleep(1);
  477 
  478         rc = setup_session();
  479         rc2 = stop_event_loop();
  480         /*
  481          * something horrible happened. kill child and get
  482          * out of here
  483          */
  484         if (rc2)
  485             kill(pid, SIGTERM);
  486 
  487         waitpid(pid, &status, WUNTRACED);
  488         if (rc || rc2)
  489             exit(ISCSI_ERR);
  490 
  491         log_debug(1, "iscsi parent done");
  492         exit(0);
  493     }
  494 
  495     pid = getpid();
  496     iscsid_set_namespace(pid);
  497 
  498     mgmt_ipc_fd = mgmt_ipc_listen();
  499     if (mgmt_ipc_fd  < 0) {
  500         log_error("Could not setup mgmt ipc");
  501         exit(ISCSI_ERR_NOMEM);
  502     }
  503 
  504     control_fd = ipc->ctldev_open();
  505     if (control_fd < 0)
  506         exit(ISCSI_ERR_NOMEM);
  507 
  508     memset(&daemon_config, 0, sizeof (daemon_config));
  509     daemon_config.initiator_name = initiatorname;
  510     /* optional InitiatorAlias */
  511     memset(&host_info, 0, sizeof (host_info));
  512     if (uname(&host_info) >= 0)
  513         daemon_config.initiator_alias = host_info.nodename;
  514 
  515     log_debug(1, "InitiatorName=%s", daemon_config.initiator_name);
  516     log_debug(1, "InitiatorAlias=%s", daemon_config.initiator_alias);
  517     log_debug(1, "TargetName=%s", config_rec.name);
  518     log_debug(1, "TPGT=%d", config_rec.tpgt);
  519     log_debug(1, "IP Address=%s", config_rec.conn[0].address);
  520 
  521     /* log the version, so that we can tell if the daemon and kernel module
  522      * match based on what shows up in the syslog.  Tarballs releases
  523      * always install both, but Linux distributors may put the kernel module
  524      * in a different RPM from the daemon and utils, and users may try to
  525      * mix and match in ways that don't work.
  526      */
  527     log_error("version %s", ISCSI_VERSION_STR);
  528 
  529     /* oom-killer will not kill us at the night... */
  530     if (oom_adjust())
  531         log_debug(1, "can not adjust oom-killer's pardon");
  532 
  533     /*
  534      * Start Main Event Loop
  535      */
  536     iscsi_initiator_init();
  537     event_loop(ipc, control_fd, mgmt_ipc_fd);
  538     ipc->ctldev_close();
  539     mgmt_ipc_close(mgmt_ipc_fd);
  540     free_initiator();
  541     sysfs_cleanup();
  542 
  543     log_debug(1, "iscsi child done");
  544     return 0;
  545 }