Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

cmd_chreipl.c

/*
 * ipl_tools - Linux for System z reipl and shutdown tools
 *
 * Command: chreipl
 *
 * Copyright IBM Corp. 2008, 2010
 * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
 *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
 */


#include "ipl_tools.h"
#include "zt_common.h"
#include "proc.h"

#define BOOTPARMS_NSS_MAX     56
#define BOOTPARMS_CCW_MAX     64
#define BOOTPARMS_FCP_MAX     3452

enum target_type {
      TT_CCW,
      TT_FCP,
      TT_NSS,
      TT_NODE,
};

enum reipl_type {
      REIPL_FCP,
      REIPL_CCW,
      REIPL_NSS
};

static const char *const usage_chreipl =
"Usage: %s [TARGET] [ARGS] [OPTIONS]\n"
"\n"
" chreipl [ccw] [-d] <DEVICE> [OPTIONS]\n"
" chreipl [fcp] [-d] <DEVICE> [-w] <WWPN> [-l] <LUN> [OPTIONS]\n"
" chreipl [node] <NODE|DIR> [OPTIONS]\n"
" chreipl nss [-n] <NAME> [OPTIONS]\n"
" chreipl [-h] [-v]\n"
"\n"
"The following re-IPL targets are supported:\n"
"  ccw      IPL from CCW device\n"
"  fcp      IPL from FCP device\n"
"  nss      IPL from NSS\n"
"  node     IPL from device specified by device node or directory\n"
"\n"
"General options:\n"
"  -p, --bootparms <PARMS> Boot parameter specification\n"
"  -h, --help              Print this help, then exit\n"
"  -v, --version           Print version information, then exit\n"
"\n"
"Options for ccw target:\n"
"  -d, --device <DEVICE>   Device number of the CCW IPL device\n"
"  -L, --loadparm <PARM>   Loadparm specification\n"
"\n"
"Options for fcp target:\n"
"  -d, --device <DEVICE>   Device number of the adapter of the FCP IPL device\n"
"  -l  --lun <LUN>         Logical unit number of the FCP IPL device\n"
"  -w  --wwpn <WWPN>       World Wide Port Name of the FCP IPL device\n"
"  -b, --bootprog <BPROG>  Bootprog specification\n"
"\n"
"Options for nss target:\n"
"  -n, --name <NAME>       Identifier of the NSS\n"
"\n"
"Options for node target:\n"
"  Depending on underlying target type (ccw or fcp)\n";

00069 static struct locals {
      char              loadparm[9];      /* Entry in the boot menu */
      int               loadparm_set;
      char              bootprog[11];     /* bootprog number (32 bit)*/
      int               bootprog_set;
      char              wwpn[20];   /* 18 character +0x" */
      int               wwpn_set;
      char              lun[20];    /* 18 character +0x" */
      int               lun_set;
      char              busid[9];   /* Bus ID e.g. 0.0.4711 */
      int               busid_set;
      char              dev[15];    /* Device (e.g. dasda) */
      int               dev_set;
      char              nss_name[9];      /* NSS name */
      int               nss_name_set;
      char              bootparms[4096];
      int               bootparms_set;
      enum target_type  target_type;      /* CCW, FCP, NSS or NODE */
      int               target_type_set;
      int               target_type_auto_mode;
      enum reipl_type         reipl_type; /* CCW, FCP, NSS */
} l;

static void print_usage_chreipl_exit(void)
{
      printf(usage_chreipl, g.prog_name);
      exit(0);
}

static int mk_busid(char busid[9], const char *devno)
{
      int devno_int, cs1, cs2;

      if (strlen(devno) > 8)
            return -1;
      if (sscanf(devno, "%x.%x.%x", &cs1, &cs2, &devno_int) == 3) {
            snprintf(busid, 9, "%x.%x.%04x",
                   cs1, cs2, devno_int);
            busid[8] = 0;
            return 0;
      }
      if (sscanf(devno, "%x", &devno_int) == 1) {
            snprintf(busid, 9, "0.0.%04x", devno_int);
            busid[8] = 0;
            return 0;
      }
      return -1;
}

static void set_device(const char *busid)
{
      if (mk_busid(l.busid, busid))
            ERR_EXIT("Invalid device number \"%s\" specified", busid);
      l.busid_set = 1;
}

static void set_nss_name(const char *nss_name)
{
      if (strlen(nss_name) > 8)
            ERR_EXIT("NSS name \"%s\" exceeds maximum of 8 characters",
                   nss_name);
      strcpy(l.nss_name, nss_name);
      l.nss_name_set = 1;
}

static void set_loadparm(const char *loadparm)
{
      if (strlen(loadparm) > 8)
            ERR_EXIT("Loadparm \"%s\" exceeds 8 characters", loadparm);
      strcpy(l.loadparm, loadparm);
      if (strcmp(l.loadparm, " ") == 0)
            l.loadparm[0] = '\0';
      l.loadparm_set = 1;
}

static void set_bootprog(const char *bootprog)
{
      long long bootprog_int;
      char *endptr;

      bootprog_int = strtoll(bootprog, &endptr, 10);
      if (*endptr)
            ERR_EXIT("Bootprog \"%s\" is not a decimal number", bootprog);
      if (bootprog_int > UINT_MAX)
            ERR_EXIT("Invalid bootprog specified");
      strncpy(l.bootprog, bootprog, sizeof(l.bootprog));
      l.bootprog_set = 1;
}

static void set_bootparms(const char *bootparms)
{
      unsigned int i;

      for (i = 0; i < strlen(bootparms); i++) {
            if (isascii(bootparms[i]))
                  continue;
            ERR_EXIT("Non ASCII characters found in boot parameters");
      }
      if (strlen(bootparms) + 1 > sizeof(l.bootparms))
            ERR_EXIT("Boot parameter line is too long");
      strcpy(l.bootparms, bootparms);
      l.bootparms_set = 1;
}

static void set_lun(const char *lun)
{
      unsigned long long lun_tmp;
      char *endptr;

      lun_tmp = strtoull(lun, &endptr, 16);
      if (*endptr)
            ERR_EXIT("LUN \"%s\" is not a hexadecimal number", lun);
      snprintf(l.lun, sizeof(l.lun), "0x%016llx", lun_tmp);
      l.lun_set = 1;
}

static void set_wwpn(const char *wwpn)
{
      unsigned long long wwpn_tmp;
      char *endptr;

      wwpn_tmp = strtoull(wwpn, &endptr, 16);
      if (*endptr)
            ERR_EXIT("WWPN \"%s\" is not a hexadecimal number", wwpn);
      snprintf(l.wwpn, sizeof(l.wwpn), "0x%016llx", wwpn_tmp);
      l.wwpn_set = 1;
}

static void parse_fcp_args(char *nargv[], int nargc)
{
      /*
       * we might be called like this:
       * chreipl fcp 4711 0x12345... 0x12345...
       */
      if (l.busid_set || l.wwpn_set || l.lun_set)
            ERR_EXIT("Use either options or positional parameters");
      if (nargc > 3)
            ERR_EXIT("Too many arguments specified for \"fcp\" re-IPL "
                   "type");
      else if (nargc != 3)
            ERR_EXIT("The \"fcp\" re-IPL type requires device, WWPN, "
                   "and LUN");
      set_device(nargv[0]);
      set_wwpn(nargv[1]);
      set_lun(nargv[2]);
}

static void parse_ccw_args(char *nargv[], int nargc)
{
      /*
       * we might be called like this:
       * chreipl ccw 4711
       */
      if (l.busid_set)
            ERR_EXIT("Use either options or positional parameters");
      if (nargc == 0)
            ERR_EXIT("The \"ccw\" re-IPL type requires device");
      else if (nargc > 1)
            ERR_EXIT("Too many arguments specified for \"ccw\" re-IPL "
                   "type");
      set_device(nargv[0]);
}

static void parse_nss_args(char *nargv[], int nargc)
{
      /*
       * we might be called like this:
       * chreipl nss lnxnss
       */
      if (l.nss_name_set)
            ERR_EXIT("Use either options or positional parameters");
      if (nargc == 0)
            ERR_EXIT("A NSS name must be specified");
      if (nargc > 1)
            ERR_EXIT("Too many arguments specified for \"nss\" re-IPL "
                   "type");
      set_nss_name(nargv[0]);
}

static void dev_from_part(char *dev_name)
{
      int i;

      for (i = strlen(dev_name) - 1; isdigit(dev_name[i]); i--)
            dev_name[i] = 0;
}

static int set_reipl_type(const char *dev_name)
{
      if (strncmp(dev_name, "dasd", strlen("dasd")) == 0)
            l.reipl_type = REIPL_CCW;
      else if (strncmp(dev_name, "sd", strlen("sd")) == 0)
            l.reipl_type = REIPL_FCP;
      else
            return -1;

      strncpy(l.dev, dev_name, sizeof(l.dev));
      dev_from_part(l.dev);
      l.dev_set = 1;
      return 0;
}

static int get_chreipl_helper_cmd(dev_t dev, char cmd[PATH_MAX])
{
      char chreipl_helper[PATH_MAX];
      struct proc_dev_entry pde;

      if (proc_dev_get_entry(dev, 1, &pde) != 0)
            return -1;
      snprintf(chreipl_helper, PATH_MAX,
             "%s/%s.%s", TOOLS_LIBDIR, "chreipl_helper", pde.name);
      if (access(chreipl_helper, X_OK) != 0) {
            proc_dev_free_entry(&pde);
            return -1;
      }
      sprintf(cmd, "%s %d:%d", chreipl_helper, major(dev), minor(dev));
      proc_dev_free_entry(&pde);
      return 0;
}

/*
 * Use chreipl_helper (E.g. for device mapper devices)
 */
static int set_reipl_type_helper(int maj, int min)
{
      char helper_cmd[PATH_MAX], buf[4096];
      struct proc_part_entry ppe;
      int rc = -1;
      dev_t dev;
      FILE *fh;

      if (get_chreipl_helper_cmd(makedev(maj, min), helper_cmd) != 0)
            return -1;
      fh = popen(helper_cmd, "r");
      if (fh == NULL)
            ERR_EXIT_ERRNO("Could not start chreipl_helper");
      if (fread(buf, 1, sizeof(buf), fh) == 0)
            ERR_EXIT_ERRNO("Could not read from chreipl_helper");

      if (sscanf(buf, "%d:%d", &maj, &min) != 2)
            goto fail_pclose;
      dev = makedev(maj, min);
      if (proc_part_get_entry(dev, &ppe) != 0)
            goto fail_pclose;
      if (set_reipl_type(ppe.name))
            goto fail_part_free;
      rc = 0;
fail_part_free:
      proc_part_free_entry(&ppe);
fail_pclose:
      pclose(fh);
      return rc;
}

static void get_dev_by_path(const char *path, dev_t *dev)
{
      struct stat sb;

      if (stat(path, &sb) != 0)
            ERR_EXIT_ERRNO("Could not access device node \"%s\"", path);
      if (S_ISDIR(sb.st_mode))
            *dev = sb.st_dev;
      else if (S_ISBLK(sb.st_mode))
            *dev = sb.st_rdev;
      else
            ERR_EXIT("Only block device nodes or directories are valid for"
                   " \"node\" target");
}

static void parse_node_args(char *nargv[], int nargc)
{
      struct proc_part_entry ppe;
      char *path = nargv[0];
      dev_t dev;

      if (nargc == 0)
            ERR_EXIT("No device node specified");
      if (l.busid_set || l.wwpn_set || l.lun_set)
            ERR_EXIT("Do not use device, WWPN, or LUN for \"node\" target");

      get_dev_by_path(path, &dev);
      if (proc_part_get_entry(dev, &ppe) != 0)
            ERR_EXIT("Invalid device node \"%s\" specified", path);
      if (set_reipl_type(ppe.name) == 0)
            goto out;
      if (set_reipl_type_helper(major(dev), minor(dev)) == 0)
            goto out;
      ERR_EXIT("Unsupported device node \"%s\" specified", path);
out:
      proc_part_free_entry(&ppe);
}

static void parse_pos_args(char *nargv[], int nargc)
{
      switch (l.target_type) {
      case TT_FCP:
            parse_fcp_args(nargv, nargc);
            break;
      case TT_CCW:
            parse_ccw_args(nargv, nargc);
            break;
      case TT_NSS:
            parse_nss_args(nargv, nargc);
            break;
      case TT_NODE:
            parse_node_args(nargv, nargc);
            break;
      }
}

static void check_fcp_opts(void)
{
      if (l.loadparm_set || l.nss_name_set)
            ERR_EXIT("Invalid option for \"fcp\" target specified");
      if (!(l.busid_set && l.wwpn_set && l.lun_set))
            ERR_EXIT("The \"fcp\" target requires device, WWPN, "
                   "and LUN");
}

static void check_ccw_opts(void)
{
      if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set)
            ERR_EXIT("Invalid option for \"ccw\" target specified");
      if (!l.busid_set)
            ERR_EXIT("The \"ccw\" target requires device");
}

static void check_nss_opts(void)
{
      if (l.bootprog_set || l.loadparm_set || l.busid_set || l.wwpn_set ||
          l.lun_set)
            ERR_EXIT("Invalid option for \"nss\" target specified");
      if (!l.nss_name_set)
            ERR_EXIT("The \"nss\" target requires NSS name");
}

static void set_target_type(enum target_type tt, int mode_auto)
{
      l.target_type = tt;
      l.target_type_set = 1;
      l.target_type_auto_mode = mode_auto;
}

static void set_target_type_auto(const char *arg)
{
      char busid[9];

      if (access(arg, R_OK) == 0) {
            set_target_type(TT_NODE, 1);
            return;
      }
      if (mk_busid(busid, arg) == 0) {
            if (ccw_is_device(busid))
                  set_target_type(TT_CCW, 1);
            else if (fcp_is_device(busid))
                  set_target_type(TT_FCP, 1);
      }
}

static void parse_chreipl_options(int argc, char *argv[])
{
      int opt, idx;
      const struct option long_opts[] = {
            { "help",    no_argument,           NULL, 'h'},
            { "bootprog",      required_argument,     NULL, 'b' },
            { "device",  required_argument,     NULL, 'd' },
            { "lun",     required_argument,     NULL, 'l' },
            { "wwpn",    required_argument,     NULL, 'w' },
            { "loadparm",      required_argument,     NULL, 'L' },
            { "name",    required_argument,     NULL, 'n' },
            { "bootparms",     required_argument,     NULL, 'p' },
            { "version",       no_argument,           NULL, 'v' },
            { NULL,            0,               NULL,  0  }
      };
      static const char optstr[] = "hcd:vw:l:f:L:b:n:p:";

      /* dont run without any argument */
      if (argc == 1)
            print_usage_chreipl_exit();

      if (strcmp(argv[1], "fcp") == 0)
            set_target_type(TT_FCP, 0);
      else if (strcmp(argv[1], "ccw") == 0)
            set_target_type(TT_CCW, 0);
      else if (strcmp(argv[1], "nss") == 0)
            set_target_type(TT_NSS, 0);
      else if (strcmp(argv[1], "node") == 0)
            set_target_type(TT_NODE, 0);
      else
            set_target_type_auto(argv[1]);

      while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) {
            switch (opt) {
            case 'h':
                  print_usage_chreipl_exit();
            case 'd':
                  set_device(optarg);
                  break;
            case 'l':
                  set_lun(optarg);
                  break;
            case 'w':
                  set_wwpn(optarg);
                  break;
            case 'L':
                  set_loadparm(optarg);
                  break;
            case 'b':
                  set_bootprog(optarg);
                  break;
            case 'n':
                  set_nss_name(optarg);
                  break;
            case 'p':
                  set_bootparms(optarg);
                  break;
            case 'v':
                  print_version_exit();
            default:
                  print_help_hint_exit();
            }
      }
      if (!is_root())
            ERR_EXIT("You must be root to perform this operation");
      if (!l.target_type_set)
            ERR_EXIT("No valid target specified");
      /*
       * optind is a index which points to the first unrecognized
       * command line argument. In case of no auto action we have to
       * skip the action argument.
       */
      if (!l.target_type_auto_mode)
            optind += 1;
      if (argc - optind > 0)
            parse_pos_args(&argv[optind], argc - optind);
}

static void verify_write_access(const char *path, const char *attr)
{
      char fpath[PATH_MAX];

      snprintf(fpath, sizeof(fpath), "/sys/firmware/%s", path);
      if (access(fpath, W_OK) != 0)
            ERR_EXIT("System does not allow to set %s", attr);
}

static void chreipl_ccw(void)
{
      check_ccw_opts();

      if (!ccw_is_device(l.busid))
            ERR_EXIT("Could not find DASD CCW device \"%s\"", l.busid);
      /*
       * On old systems that use CCW reipl loadparm cannot be set
       */
      if (l.loadparm_set)
            verify_write_access("reipl/ccw/loadparm", "loadparm");
      if (l.bootparms_set) {
            verify_write_access("reipl/ccw/parm", "boot parameters");
            if (strlen(l.bootparms) > BOOTPARMS_CCW_MAX)
                  ERR_EXIT("Maximum boot parameter length exceeded "
                         "(%zu/%u)", strlen(l.bootparms),
                         BOOTPARMS_CCW_MAX);
      }
      if (access("/sys/firmware/reipl/ccw/parm", W_OK) == 0)
            write_str(l.bootparms, "reipl/ccw/parm");
      if (access("/sys/firmware/reipl/ccw/loadparm", W_OK) == 0)
            write_str(l.loadparm, "reipl/ccw/loadparm");
      write_str(l.busid, "reipl/ccw/device");
      write_str("ccw", "reipl/reipl_type");

      print_ccw(0);
}

static void chreipl_fcp(void)
{
      check_fcp_opts();
      if (!fcp_is_device(l.busid))
            ERR_EXIT("Could not find FCP device \"%s\"", l.busid);
      verify_write_access("reipl/fcp/device", "\"fcp\" re-IPL target");
      if (l.bootparms_set) {
            verify_write_access("reipl/fcp/scp_data", "boot parameters");
            if (strlen(l.bootparms) > BOOTPARMS_FCP_MAX)
                  ERR_EXIT("Maximum boot parameter length exceeded "
                         "(%zu/%u)", strlen(l.bootparms),
                         BOOTPARMS_FCP_MAX);
      }
      if (access("/sys/firmware/reipl/fcp/scp_data", W_OK) == 0)
            write_str(l.bootparms, "reipl/fcp/scp_data");
      write_str(l.busid, "reipl/fcp/device");
      write_str(l.wwpn, "reipl/fcp/wwpn");
      write_str(l.lun, "reipl/fcp/lun");
      write_str("fcp", "reipl/reipl_type");
      /*
       * set the boot record logical block address. Master boot
       * record. It is always 0 for Linux
       */
      write_str("0", "reipl/fcp/br_lba");
      if (!l.bootprog_set)
            sprintf(l.bootprog, "0");
      write_str(l.bootprog,  "reipl/fcp/bootprog");

      print_fcp(0);
}

static void chreipl_nss(void)
{
      check_nss_opts();
      verify_write_access("reipl/nss/name", "\"nss\" re-IPL target");
      if (l.bootparms_set) {
            verify_write_access("reipl/nss/parm", "boot parameters");
            if (strlen(l.bootparms) > BOOTPARMS_NSS_MAX)
                  ERR_EXIT("Maximum boot parameter length exceeded "
                         "(%zu/%u)", strlen(l.bootparms),
                         BOOTPARMS_NSS_MAX);
      }
      write_str(l.nss_name, "reipl/nss/name");
      if (access("/sys/firmware/reipl/nss/parm", W_OK) == 0)
            write_str(l.bootparms, "reipl/nss/parm");
      write_str("nss", "reipl/reipl_type");
      print_nss(0);
}

static void chreipl_node(void)
{
      char path[PATH_MAX];

      if (!l.dev_set)
            ERR_EXIT("No device node specified");
      snprintf(path, sizeof(path), "/sys/block/%s/device", l.dev);
      if (chdir(path) != 0)
            ERR_EXIT("Could not find device \"%s\"", l.dev);

      switch (l.reipl_type) {
      case REIPL_CCW:
            ccw_busid_get(l.dev, l.busid);
            l.busid_set = 1;
            chreipl_ccw();
            break;
      case REIPL_FCP:
            fcp_wwpn_get(l.dev, l.wwpn);
            l.wwpn_set = 1;
            fcp_lun_get(l.dev, l.lun);
            l.lun_set = 1;
            fcp_busid_get(l.dev, l.busid);
            l.busid_set = 1;
            chreipl_fcp();
            break;
      default:
            ERR_EXIT("Internal error: chreipl_node");
      }
}

void cmd_chreipl(int argc, char *argv[])
{
      parse_chreipl_options(argc, argv);
      switch (l.target_type) {
      case TT_CCW:
            chreipl_ccw();
            break;
      case TT_FCP:
            chreipl_fcp();
            break;
      case TT_NSS:
            chreipl_nss();
            break;
      case TT_NODE:
            chreipl_node();
            break;
      }
}

Generated by  Doxygen 1.6.0   Back to index