summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorMarcelo Araujo <araujo@FreeBSD.org>2018-05-10 03:50:20 +0000
committerMarcelo Araujo <araujo@FreeBSD.org>2018-05-10 03:50:20 +0000
commit8951f05525ee4e9a93cc568dccd154405aae7419 (patch)
tree503e8ca76afeb0231224fd5f2522663ca24ca5f6 /usr.sbin
parent3429b518c9bfbce5a16d76949d75a843567ba2de (diff)
downloadsrc-test2-8951f05525ee4e9a93cc568dccd154405aae7419.tar.gz
src-test2-8951f05525ee4e9a93cc568dccd154405aae7419.zip
Notes
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ctladm/Makefile2
-rw-r--r--usr.sbin/ctladm/ctladm.866
-rw-r--r--usr.sbin/ctladm/ctladm.c339
-rw-r--r--usr.sbin/ctld/Makefile2
-rw-r--r--usr.sbin/ctld/ctld.c52
-rw-r--r--usr.sbin/ctld/ctld.h5
-rw-r--r--usr.sbin/ctld/kernel.c193
-rw-r--r--usr.sbin/ctld/parse.y55
-rw-r--r--usr.sbin/ctld/uclparse.c13
9 files changed, 422 insertions, 305 deletions
diff --git a/usr.sbin/ctladm/Makefile b/usr.sbin/ctladm/Makefile
index 5b74668bf571..9f7386bdc36c 100644
--- a/usr.sbin/ctladm/Makefile
+++ b/usr.sbin/ctladm/Makefile
@@ -16,7 +16,7 @@ CFLAGS+= -I${SDIR}
WARNS?= 3
.endif
-LIBADD= cam sbuf bsdxml util
+LIBADD= cam sbuf bsdxml util nv
MAN= ctladm.8
.if ${MK_ISCSI} != "no"
diff --git a/usr.sbin/ctladm/ctladm.8 b/usr.sbin/ctladm/ctladm.8
index e9dd3620d99f..d9b0c075eeed 100644
--- a/usr.sbin/ctladm/ctladm.8
+++ b/usr.sbin/ctladm/ctladm.8
@@ -1,6 +1,7 @@
.\"
.\" Copyright (c) 2003 Silicon Graphics International Corp.
.\" Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
+.\" Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -35,7 +36,7 @@
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
.\" $FreeBSD$
.\"
-.Dd July 15, 2017
+.Dd May 10, 2018
.Dt CTLADM 8
.Os
.Sh NAME
@@ -161,10 +162,13 @@
.Op Fl x
.Nm
.Ic port
+.Op Fl c
.Op Fl o Ar on|off
.Op Fl w Ar wwpn
.Op Fl W Ar wwnn
+.Op Fl O Ar pp|vp
.Op Fl p Ar targ_port
+.Op Fl r Ar targ_port
.Op Fl t Ar fe_type
.Nm
.Ic portlist
@@ -593,13 +597,21 @@ must be specified.
The WWNN and WWPN may both be specified at the same time, but cannot be
combined with enabling/disabling or listing ports.
.Bl -tag -width 12n
+.It Fl c
+Create new frontend port using free pp and vp=0.
.It Fl o Ar on|off
-Turn the specified CTL frontend ports off or on.
+Turn the specified CTL frontend ports on or off.
If no port number or port type is specified, all ports are turned on or
off.
+.It Fl O Ar pp|vp
+Specify generic options on the ioctl frontend port.
+At present, only pp and vp port numbers can be set.
.It Fl p Ar targ_port
Specify the frontend port number.
The port numbers can be found in the frontend port list.
+.It Fl r
+Remove port specified with
+.Pq Fl p Ar targ_port .
.It Fl t Ar fe_type
Specify the frontend type.
Currently defined port types are
@@ -950,29 +962,26 @@ The default value is zero, that disables backing store completely,
making all writes go to nowhere, while all reads return zeroes.
.El
.Sh EXAMPLES
-.Dl ctladm tur 1
.Pp
Send a
.Tn SCSI
TEST UNIT READY command to LUN 1.
.Pp
-.Dl ctladm modesense 1 -l
+.Dl ctladm tur 1
.Pp
Display the list of mode pages supported by LUN 1.
.Pp
-.Dl ctladm modesense 0 -m 10 -P 3 -d -c 10
+.Dl ctladm modesense 1 -l
.Pp
Display the saved version of the Control mode page (page 10) on LUN 0.
Disable fetching block descriptors, and use a 10 byte MODE SENSE command
instead of the default 6 byte command.
-.Bd -literal
-ctladm read 2 -l 0 -d 1 -b 512 -f - > foo
-.Ed
+.Pp
+.Dl ctladm modesense 0 -m 10 -P 3 -d -c 10
.Pp
Read the first 512 byte block from LUN 2 and dump it to the file
-.Pa foo .
.Bd -literal
-ctladm write 3 -l 0xff432140 -d 20 -b 512 -f /tmp/bar
+.Dl ctladm read 2 -l 0 -d 1 -b 512 -f - > foo
.Ed
.Pp
Read 10240 bytes from the file
@@ -980,7 +989,9 @@ Read 10240 bytes from the file
and write it to LUN 3.
starting at LBA 0xff432140.
.Pp
-.Dl ctladm create -b ramdisk -s 10485760000000000
+.Bd -literal
+.Dl ctladm write 3 -l 0xff432140 -d 20 -b 512 -f /tmp/bar
+.Ed
.Pp
Create a LUN with the
.Dq fake
@@ -988,20 +999,20 @@ ramdisk as a backing store.
The LUN will claim to have a size of approximately 10 terabytes,
while having no real data store (all written data are lost).
.Pp
-.Dl ctladm create -b ramdisk -s 10T -o capacity=10G
+.Dl ctladm create -b ramdisk -s 10485760000000000
.Pp
Create a thin provisioned LUN with a ramdisk as a backing store.
The LUN will have maximal backing store capacity of 10 gigabytes,
while reporting size of 10 terabytes,
.Pp
-.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8
+.Dl ctladm create -b ramdisk -s 10T -o capacity=10G
.Pp
Create a LUN using the block backend, and specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
as the backing store.
The size of the LUN will be derived from the size of the file.
.Pp
-.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8 -S MYSERIAL321 -d MYDEVID123
+.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8
.Pp
Create a LUN using the block backend, specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
@@ -1012,33 +1023,46 @@ VPD page 0x80 and 0x83 serial number
and device ID
.Fl ( d ) .
.Pp
-.Dl ctladm remove -b block -l 12
+.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8 -S MYSERIAL321 -d MYDEVID123
+.Pp
+Use to specify generic options on ioctl frontend port, now it is
+only possible to set pp and/or vp port number.
+.Pp
+.Dl ctladm port -c -O pp=11 -O vp=12
+.Pp
+Remove specified targ_port.
+.Pp
+.Dl ctladm port -r -p 4
+.Pp
.Pp
Remove LUN 12, which is handled by the block backend, from the system.
.Pp
-.Dl ctladm devlist
+.Dl ctladm remove -b block -l 12
.Pp
List configured LUNs in the system, along with their backend and serial
number.
This works when the Front End Target Drivers are enabled or disabled.
.Pp
-.Dl ctladm lunlist
+.Dl ctladm devlist
.Pp
List all LUNs in the system, along with their inquiry data and device type.
This only works when the FETDs are enabled, since the commands go through the
ioctl port.
.Pp
-.Dl ctladm inject 6 -i mediumerr -p read -r 0,512 -c
+.Dl ctladm lunlist
.Pp
Inject a medium error on LUN 6 for every read that covers the first 512
blocks of the LUN.
-.Bd -literal -offset indent
-ctladm inject 6 -i custom -p tur -s 18 "f0 0 02 s12 04 02"
-.Ed
+.Pp
+.Dl ctladm inject 6 -i mediumerr -p read -r 0,512 -c
.Pp
Inject a custom error on LUN 6 for the next TEST UNIT READY command only.
This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of
0x04,0x02 ("Logical unit not ready, initializing command required").
+.Pp
+.Bd -literal -offset indent
+ctladm inject 6 -i custom -p tur -s 18 "f0 0 02 s12 04 02"
+.Ed
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam_cdbparse 3 ,
diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c
index 8bad0452b4a8..2761b2c763b0 100644
--- a/usr.sbin/ctladm/ctladm.c
+++ b/usr.sbin/ctladm/ctladm.c
@@ -1,7 +1,10 @@
/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* Copyright (c) 1997-2007 Kenneth D. Merry
* Copyright (c) 2012 The FreeBSD Foundation
+ * Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
@@ -50,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
+#include <sys/nv.h>
#include <sys/stat.h>
#include <bsdxml.h>
#include <ctype.h>
@@ -178,7 +182,7 @@ static struct ctladm_opts option_table[] = {
{"lunmap", CTLADM_CMD_LUNMAP, CTLADM_ARG_NONE, "p:l:L:"},
{"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"},
{"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:o:s:"},
- {"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:p:qt:w:W:x"},
+ {"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:O:d:crp:qt:w:W:x"},
{"portlist", CTLADM_CMD_PORTLIST, CTLADM_ARG_NONE, "f:ilp:qvx"},
{"prin", CTLADM_CMD_PRES_IN, CTLADM_ARG_NEED_TL, "a:"},
{"prout", CTLADM_CMD_PRES_OUT, CTLADM_ARG_NEED_TL, "a:k:r:s:"},
@@ -369,7 +373,9 @@ typedef enum {
CCTL_PORT_MODE_LIST,
CCTL_PORT_MODE_SET,
CCTL_PORT_MODE_ON,
- CCTL_PORT_MODE_OFF
+ CCTL_PORT_MODE_OFF,
+ CCTL_PORT_MODE_CREATE,
+ CCTL_PORT_MODE_REMOVE
} cctl_port_mode;
static struct ctladm_opts cctl_fe_table[] = {
@@ -392,9 +398,16 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
uint64_t wwnn = 0, wwpn = 0;
cctl_port_mode port_mode = CCTL_PORT_MODE_NONE;
struct ctl_port_entry entry;
+ struct ctl_req req;
+ char *driver = NULL;
+ nvlist_t *option_list;
ctl_port_type port_type = CTL_PORT_NONE;
int quiet = 0, xml = 0;
+ option_list = nvlist_create(0);
+ if (option_list == NULL)
+ err(1, "%s: unable to allocate nvlist", __func__);
+
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
case 'l':
@@ -403,6 +416,12 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
port_mode = CCTL_PORT_MODE_LIST;
break;
+ case 'c':
+ port_mode = CCTL_PORT_MODE_CREATE;
+ break;
+ case 'r':
+ port_mode = CCTL_PORT_MODE_REMOVE;
+ break;
case 'o':
if (port_mode != CCTL_PORT_MODE_NONE)
goto bailout_badarg;
@@ -419,6 +438,40 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
goto bailout;
}
break;
+ case 'O': {
+ char *tmpstr;
+ char *name, *value;
+
+ tmpstr = strdup(optarg);
+ name = strsep(&tmpstr, "=");
+ if (name == NULL) {
+ warnx("%s: option -O takes \"name=value\""
+ "argument", __func__);
+ retval = 1;
+ goto bailout;
+ }
+ value = strsep(&tmpstr, "=");
+ if (value == NULL) {
+ warnx("%s: option -O takes \"name=value\""
+ "argument", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ free(tmpstr);
+ nvlist_add_string(option_list, name, value);
+ break;
+ }
+ case 'd':
+ if (driver != NULL) {
+ warnx("%s: option -d cannot be specified twice",
+ __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ driver = strdup(optarg);
+ break;
case 'p':
targ_port = strtol(optarg, NULL, 0);
break;
@@ -474,6 +527,9 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
}
}
+ if (driver == NULL)
+ driver = strdup("ioctl");
+
/*
* The user can specify either one or more frontend types (-t), or
* a specific frontend, but not both.
@@ -515,6 +571,56 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
cctl_portlist(fd, argcx, argvx, opts);
break;
}
+ case CCTL_PORT_MODE_REMOVE:
+ if (targ_port == -1) {
+ warnx("%s: -r require -p", __func__);
+ retval = 1;
+ goto bailout;
+ }
+ case CCTL_PORT_MODE_CREATE: {
+ bzero(&req, sizeof(req));
+ strlcpy(req.driver, driver, sizeof(req.driver));
+
+ if (port_mode == CCTL_PORT_MODE_REMOVE) {
+ req.reqtype = CTL_REQ_REMOVE;
+ nvlist_add_stringf(option_list, "port_id", "%d",
+ targ_port);
+ } else
+ req.reqtype = CTL_REQ_CREATE;
+
+ req.args = nvlist_pack(option_list, &req.args_len);
+ if (req.args == NULL) {
+ warn("%s: error packing nvlist", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ retval = ioctl(fd, CTL_PORT_REQ, &req);
+ free(req.args);
+ if (retval == -1) {
+ warn("%s: CTL_PORT_REQ ioctl failed", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ switch (req.status) {
+ case CTL_LUN_ERROR:
+ warnx("error: %s", req.error_str);
+ retval = 1;
+ goto bailout;
+ case CTL_LUN_WARNING:
+ warnx("warning: %s", req.error_str);
+ break;
+ case CTL_LUN_OK:
+ break;
+ default:
+ warnx("unknown status: %d", req.status);
+ retval = 1;
+ goto bailout;
+ }
+
+ break;
+ }
case CCTL_PORT_MODE_SET:
if (targ_port == -1) {
warnx("%s: -w and -W require -n", __func__);
@@ -561,7 +667,8 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
}
bailout:
-
+ nvlist_destroy(req.args_nvl);
+ free(driver);
return (retval);
bailout_badarg:
@@ -2271,14 +2378,6 @@ bailout:
return (retval);
}
-struct cctl_req_option {
- char *name;
- int namelen;
- char *value;
- int vallen;
- STAILQ_ENTRY(cctl_req_option) links;
-};
-
static int
cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
{
@@ -2290,11 +2389,12 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
char *device_id = NULL;
int lun_size_set = 0, blocksize_set = 0, lun_id_set = 0;
char *backend_name = NULL;
- STAILQ_HEAD(, cctl_req_option) option_list;
- int num_options = 0;
+ nvlist_t *option_list;
int retval = 0, c;
- STAILQ_INIT(&option_list);
+ option_list = nvlist_create(0);
+ if (option_list == NULL)
+ err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -2313,7 +2413,6 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
- struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@@ -2332,21 +2431,8 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
- option = malloc(sizeof(*option));
- if (option == NULL) {
- warn("%s: error allocating %zd bytes",
- __func__, sizeof(*option));
- retval = 1;
- goto bailout;
- }
- option->name = strdup(name);
- option->namelen = strlen(name) + 1;
- option->value = strdup(value);
- option->vallen = strlen(value) + 1;
free(tmpstr);
-
- STAILQ_INSERT_TAIL(&option_list, option, links);
- num_options++;
+ nvlist_add_string(option_list, name, value);
break;
}
case 's':
@@ -2413,43 +2499,16 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
}
- req.num_be_args = num_options;
- if (num_options > 0) {
- struct cctl_req_option *option, *next_option;
- int i;
-
- req.be_args = malloc(num_options * sizeof(*req.be_args));
- if (req.be_args == NULL) {
- warn("%s: error allocating %zd bytes", __func__,
- num_options * sizeof(*req.be_args));
- retval = 1;
- goto bailout;
- }
-
- for (i = 0, option = STAILQ_FIRST(&option_list);
- i < num_options; i++, option = next_option) {
- next_option = STAILQ_NEXT(option, links);
-
- req.be_args[i].namelen = option->namelen;
- req.be_args[i].name = strdup(option->name);
- req.be_args[i].vallen = option->vallen;
- req.be_args[i].value = strdup(option->value);
- /*
- * XXX KDM do we want a way to specify a writeable
- * flag of some sort? Do we want a way to specify
- * binary data?
- */
- req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
-
- STAILQ_REMOVE(&option_list, option, cctl_req_option,
- links);
- free(option->name);
- free(option->value);
- free(option);
- }
+ req.args = nvlist_pack(option_list, &req.args_len);
+ if (req.args == NULL) {
+ warn("%s: error packing nvlist", __func__);
+ retval = 1;
+ goto bailout;
}
- if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
+ retval = ioctl(fd, CTL_LUN_REQ, &req);
+ free(req.args);
+ if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@@ -2480,9 +2539,10 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.create.blocksize_bytes);
fprintf(stdout, "LUN ID: %d\n", req.reqdata.create.req_lun_id);
fprintf(stdout, "Serial Number: %s\n", req.reqdata.create.serial_num);
- fprintf(stdout, "Device ID; %s\n", req.reqdata.create.device_id);
+ fprintf(stdout, "Device ID: %s\n", req.reqdata.create.device_id);
bailout:
+ nvlist_destroy(req.args_nvl);
return (retval);
}
@@ -2493,11 +2553,12 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
uint32_t lun_id = 0;
int lun_id_set = 0;
char *backend_name = NULL;
- STAILQ_HEAD(, cctl_req_option) option_list;
- int num_options = 0;
+ nvlist_t *option_list;
int retval = 0, c;
- STAILQ_INIT(&option_list);
+ option_list = nvlist_create(0);
+ if (option_list == NULL)
+ err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@@ -2509,7 +2570,6 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
- struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@@ -2528,21 +2588,8 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
- option = malloc(sizeof(*option));
- if (option == NULL) {
- warn("%s: error allocating %zd bytes",
- __func__, sizeof(*option));
- retval = 1;
- goto bailout;
- }
- option->name = strdup(name);
- option->namelen = strlen(name) + 1;
- option->value = strdup(value);
- option->vallen = strlen(value) + 1;
free(tmpstr);
-
- STAILQ_INSERT_TAIL(&option_list, option, links);
- num_options++;
+ nvlist_add_string(option_list, name, value);
break;
}
default:
@@ -2562,44 +2609,17 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqtype = CTL_LUNREQ_RM;
req.reqdata.rm.lun_id = lun_id;
-
- req.num_be_args = num_options;
- if (num_options > 0) {
- struct cctl_req_option *option, *next_option;
- int i;
-
- req.be_args = malloc(num_options * sizeof(*req.be_args));
- if (req.be_args == NULL) {
- warn("%s: error allocating %zd bytes", __func__,
- num_options * sizeof(*req.be_args));
- retval = 1;
- goto bailout;
- }
-
- for (i = 0, option = STAILQ_FIRST(&option_list);
- i < num_options; i++, option = next_option) {
- next_option = STAILQ_NEXT(option, links);
-
- req.be_args[i].namelen = option->namelen;
- req.be_args[i].name = strdup(option->name);
- req.be_args[i].vallen = option->vallen;
- req.be_args[i].value = strdup(option->value);
- /*
- * XXX KDM do we want a way to specify a writeable
- * flag of some sort? Do we want a way to specify
- * binary data?
- */
- req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
-
- STAILQ_REMOVE(&option_list, option, cctl_req_option,
- links);
- free(option->name);
- free(option->value);
- free(option);
- }
+
+ req.args = nvlist_pack(option_list, &req.args_len);
+ if (req.args == NULL) {
+ warn("%s: error packing nvlist", __func__);
+ retval = 1;
+ goto bailout;
}
- if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
+ retval = ioctl(fd, CTL_LUN_REQ, &req);
+ free(req.args);
+ if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@@ -2624,6 +2644,7 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
printf("LUN %d removed successfully\n", lun_id);
bailout:
+ nvlist_destroy(req.args_nvl);
return (retval);
}
@@ -2635,11 +2656,13 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
uint32_t lun_id = 0;
int lun_id_set = 0, lun_size_set = 0;
char *backend_name = NULL;
- STAILQ_HEAD(, cctl_req_option) option_list;
- int num_options = 0;
+ nvlist_t *option_list;
int retval = 0, c;
- STAILQ_INIT(&option_list);
+ option_list = nvlist_create(0);
+ if (option_list == NULL)
+ err(1, "%s: unable to allocate nvlist", __func__);
+
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
case 'b':
@@ -2650,7 +2673,6 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
- struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@@ -2669,21 +2691,8 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
- option = malloc(sizeof(*option));
- if (option == NULL) {
- warn("%s: error allocating %zd bytes",
- __func__, sizeof(*option));
- retval = 1;
- goto bailout;
- }
- option->name = strdup(name);
- option->namelen = strlen(name) + 1;
- option->value = strdup(value);
- option->vallen = strlen(value) + 1;
free(tmpstr);
-
- STAILQ_INSERT_TAIL(&option_list, option, links);
- num_options++;
+ nvlist_add_string(option_list, name, value);
break;
}
case 's':
@@ -2709,7 +2718,7 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
if (lun_id_set == 0)
errx(1, "%s: LUN id (-l) must be specified", __func__);
- if (lun_size_set == 0 && num_options == 0)
+ if (lun_size_set == 0 && nvlist_empty(option_list))
errx(1, "%s: size (-s) or options (-o) must be specified",
__func__);
@@ -2721,43 +2730,16 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.modify.lun_id = lun_id;
req.reqdata.modify.lun_size_bytes = lun_size;
- req.num_be_args = num_options;
- if (num_options > 0) {
- struct cctl_req_option *option, *next_option;
- int i;
-
- req.be_args = malloc(num_options * sizeof(*req.be_args));
- if (req.be_args == NULL) {
- warn("%s: error allocating %zd bytes", __func__,
- num_options * sizeof(*req.be_args));
- retval = 1;
- goto bailout;
- }
-
- for (i = 0, option = STAILQ_FIRST(&option_list);
- i < num_options; i++, option = next_option) {
- next_option = STAILQ_NEXT(option, links);
-
- req.be_args[i].namelen = option->namelen;
- req.be_args[i].name = strdup(option->name);
- req.be_args[i].vallen = option->vallen;
- req.be_args[i].value = strdup(option->value);
- /*
- * XXX KDM do we want a way to specify a writeable
- * flag of some sort? Do we want a way to specify
- * binary data?
- */
- req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
-
- STAILQ_REMOVE(&option_list, option, cctl_req_option,
- links);
- free(option->name);
- free(option->value);
- free(option);
- }
+ req.args = nvlist_pack(option_list, &req.args_len);
+ if (req.args == NULL) {
+ warn("%s: error packing nvlist", __func__);
+ retval = 1;
+ goto bailout;
}
- if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
+ retval = ioctl(fd, CTL_LUN_REQ, &req);
+ free(req.args);
+ if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@@ -2782,6 +2764,7 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
printf("LUN %d modified successfully\n", lun_id);
bailout:
+ nvlist_destroy(req.args_nvl);
return (retval);
}
@@ -3661,7 +3644,7 @@ cctl_portlist(int fd, int argc, char **argv, char *combinedopt)
struct cctl_portlist_data portlist;
struct cctl_port *port;
XML_Parser parser;
- char *port_str;
+ char *port_str = NULL;
int port_len;
int dump_xml = 0;
int retval, c;
@@ -3704,7 +3687,7 @@ cctl_portlist(int fd, int argc, char **argv, char *combinedopt)
}
retry:
- port_str = malloc(port_len);
+ port_str = (char *)realloc(port_str, port_len);
bzero(&list, sizeof(list));
list.alloc_len = port_len;
@@ -3876,6 +3859,8 @@ usage(int error)
" [-s len fmt [args]] [-c] [-d delete_id]\n"
" ctladm port <-o <on|off> | [-w wwnn][-W wwpn]>\n"
" [-p targ_port] [-t port_type]\n"
+" <-c> [-d driver] [-O name=value]\n"
+" <-r> <-p targ_port>\n"
" ctladm portlist [-f frontend] [-i] [-p targ_port] [-q] [-v] [-x]\n"
" ctladm islist [-v | -x]\n"
" ctladm islogout <-a | -c connection-id | -i name | -p portal>\n"
@@ -3954,12 +3939,16 @@ usage(int error)
"-c : continuous operation\n"
"-d delete_id : error id to delete\n"
"port options:\n"
+"-c : create new ioctl or iscsi frontend port\n"
+"-d : specify ioctl or iscsi frontend type\n"
"-l : list frontend ports\n"
"-o on|off : turn frontend ports on or off\n"
+"-O pp|vp : create new frontend port using pp and/or vp\n"
"-w wwnn : set WWNN for one frontend\n"
"-W wwpn : set WWPN for one frontend\n"
"-t port_type : specify fc, scsi, ioctl, internal frontend type\n"
"-p targ_port : specify target port number\n"
+"-r : remove frontend port\n"
"-q : omit header in list output\n"
"-x : output port list in XML format\n"
"portlist options:\n"
diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile
index 944b94d663c8..2d13099c7a9a 100644
--- a/usr.sbin/ctld/Makefile
+++ b/usr.sbin/ctld/Makefile
@@ -15,7 +15,7 @@ CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
#CFLAGS+= -DICL_KERNEL_PROXY
MAN= ctld.8 ctl.conf.5
-LIBADD= bsdxml l md sbuf util ucl m
+LIBADD= bsdxml l md sbuf util ucl m nv
YFLAGS+= -v
CLEANFILES= y.tab.c y.tab.h y.output
diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c
index a728682a9e63..c1dbac6f7956 100644
--- a/usr.sbin/ctld/ctld.c
+++ b/usr.sbin/ctld/ctld.c
@@ -1234,6 +1234,7 @@ port_new(struct conf *conf, struct target *target, struct portal_group *pg)
log_err(1, "calloc");
port->p_conf = conf;
port->p_name = name;
+ port->p_ioctl_port = 0;
TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
port->p_target = target;
@@ -1243,6 +1244,51 @@ port_new(struct conf *conf, struct target *target, struct portal_group *pg)
}
struct port *
+port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp)
+{
+ struct pport *pport;
+ struct port *port;
+ char *pname;
+ char *name;
+ int ret;
+
+ ret = asprintf(&pname, "ioctl/%d/%d", pp, vp);
+ if (ret <= 0) {
+ log_err(1, "asprintf");
+ return (NULL);
+ }
+
+ pport = pport_find(conf, pname);
+ if (pport != NULL) {
+ free(pname);
+ return (port_new_pp(conf, target, pport));
+ }
+
+ ret = asprintf(&name, "%s-%s", pname, target->t_name);
+ free(pname);
+
+ if (ret <= 0)
+ log_err(1, "asprintf");
+ if (port_find(conf, name) != NULL) {
+ log_warnx("duplicate port \"%s\"", name);
+ free(name);
+ return (NULL);
+ }
+ port = calloc(1, sizeof(*port));
+ if (port == NULL)
+ log_err(1, "calloc");
+ port->p_conf = conf;
+ port->p_name = name;
+ port->p_ioctl_port = 1;
+ port->p_ioctl_pp = pp;
+ port->p_ioctl_vp = vp;
+ TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
+ TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
+ port->p_target = target;
+ return (port);
+}
+
+struct port *
port_new_pp(struct conf *conf, struct target *target, struct pport *pp)
{
struct port *port;
@@ -1627,9 +1673,9 @@ conf_print(struct conf *conf)
TAILQ_FOREACH(auth_name, &ag->ag_names, an_next)
fprintf(stderr, "\t initiator-name %s\n",
auth_name->an_initator_name);
- TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
+ TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next)
fprintf(stderr, "\t initiator-portal %s\n",
- auth_portal->an_initator_portal);
+ auth_portal->ap_initator_portal);
fprintf(stderr, "}\n");
}
TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
@@ -1643,7 +1689,7 @@ conf_print(struct conf *conf)
fprintf(stderr, "\t\tpath %s\n", lun->l_path);
TAILQ_FOREACH(o, &lun->l_options, o_next)
fprintf(stderr, "\t\toption %s %s\n",
- lo->o_name, lo->o_value);
+ o->o_name, o->o_value);
fprintf(stderr, "\t}\n");
}
TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h
index 8644a806f932..60dac68c0e0c 100644
--- a/usr.sbin/ctld/ctld.h
+++ b/usr.sbin/ctld/ctld.h
@@ -152,6 +152,9 @@ struct port {
struct pport *p_pport;
struct target *p_target;
+ int p_ioctl_port;
+ int p_ioctl_pp;
+ int p_ioctl_vp;
uint32_t p_ctl_port;
};
@@ -368,6 +371,8 @@ void pport_delete(struct pport *pport);
struct port *port_new(struct conf *conf, struct target *target,
struct portal_group *pg);
+struct port *port_new_ioctl(struct conf *conf, struct target *target,
+ int pp, int vp);
struct port *port_new_pp(struct conf *conf, struct target *target,
struct pport *pp);
struct port *port_find(const struct conf *conf, const char *name);
diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c
index f300eff35bb9..166025f4587e 100644
--- a/usr.sbin/ctld/kernel.c
+++ b/usr.sbin/ctld/kernel.c
@@ -4,6 +4,7 @@
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* Copyright (c) 1997-2007 Kenneth D. Merry
* Copyright (c) 2012 The FreeBSD Foundation
+ * Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
@@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
+#include <sys/nv.h>
#include <sys/stat.h>
#include <assert.h>
#include <bsdxml.h>
@@ -73,6 +75,8 @@ __FBSDID("$FreeBSD$");
#include <netdb.h>
#endif
+#define NVLIST_BUFSIZE 1024
+
extern bool proxy_mode;
static int ctl_fd = 0;
@@ -652,23 +656,12 @@ retry_port:
return (conf);
}
-static void
-str_arg(struct ctl_be_arg *arg, const char *name, const char *value)
-{
-
- arg->namelen = strlen(name) + 1;
- arg->name = __DECONST(char *, name);
- arg->vallen = strlen(value) + 1;
- arg->value = __DECONST(char *, value);
- arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
-}
-
int
kernel_lun_add(struct lun *lun)
{
struct option *o;
struct ctl_lun_req req;
- int error, i, num_options;
+ int error;
bzero(&req, sizeof(req));
@@ -724,29 +717,26 @@ kernel_lun_add(struct lun *lun)
assert(o != NULL);
}
- num_options = 0;
- TAILQ_FOREACH(o, &lun->l_options, o_next)
- num_options++;
-
- req.num_be_args = num_options;
- if (num_options > 0) {
- req.be_args = malloc(num_options * sizeof(*req.be_args));
- if (req.be_args == NULL) {
- log_warn("error allocating %zd bytes",
- num_options * sizeof(*req.be_args));
+ if (!TAILQ_EMPTY(&lun->l_options)) {
+ req.args_nvl = nvlist_create(0);
+ if (req.args_nvl == NULL) {
+ log_warn("error allocating nvlist");
return (1);
}
- i = 0;
- TAILQ_FOREACH(o, &lun->l_options, o_next) {
- str_arg(&req.be_args[i], o->o_name, o->o_value);
- i++;
+ TAILQ_FOREACH(o, &lun->l_options, o_next)
+ nvlist_add_string(req.args_nvl, o->o_name, o->o_value);
+
+ req.args = nvlist_pack(req.args_nvl, &req.args_len);
+ if (req.args == NULL) {
+ log_warn("error packing nvlist");
+ return (1);
}
- assert(i == num_options);
}
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
- free(req.be_args);
+ nvlist_destroy(req.args_nvl);
+
if (error != 0) {
log_warn("error issuing CTL_LUN_REQ ioctl");
return (1);
@@ -776,7 +766,7 @@ kernel_lun_modify(struct lun *lun)
{
struct option *o;
struct ctl_lun_req req;
- int error, i, num_options;
+ int error;
bzero(&req, sizeof(req));
@@ -786,29 +776,26 @@ kernel_lun_modify(struct lun *lun)
req.reqdata.modify.lun_id = lun->l_ctl_lun;
req.reqdata.modify.lun_size_bytes = lun->l_size;
- num_options = 0;
- TAILQ_FOREACH(o, &lun->l_options, o_next)
- num_options++;
-
- req.num_be_args = num_options;
- if (num_options > 0) {
- req.be_args = malloc(num_options * sizeof(*req.be_args));
- if (req.be_args == NULL) {
- log_warn("error allocating %zd bytes",
- num_options * sizeof(*req.be_args));
+ if (!TAILQ_EMPTY(&lun->l_options)) {
+ req.args_nvl = nvlist_create(0);
+ if (req.args_nvl == NULL) {
+ log_warn("error allocating nvlist");
return (1);
}
- i = 0;
- TAILQ_FOREACH(o, &lun->l_options, o_next) {
- str_arg(&req.be_args[i], o->o_name, o->o_value);
- i++;
+ TAILQ_FOREACH(o, &lun->l_options, o_next)
+ nvlist_add_string(req.args_nvl, o->o_name, o->o_value);
+
+ req.args = nvlist_pack(req.args_nvl, &req.args_len);
+ if (req.args == NULL) {
+ log_warn("error packing nvlist");
+ return (1);
}
- assert(i == num_options);
}
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
- free(req.be_args);
+ nvlist_destroy(req.args_nvl);
+
if (error != 0) {
log_warn("error issuing CTL_LUN_REQ ioctl");
return (1);
@@ -988,37 +975,54 @@ kernel_port_add(struct port *port)
struct ctl_lun_map lm;
struct target *targ = port->p_target;
struct portal_group *pg = port->p_portal_group;
- char tagstr[16];
- int error, i, n;
+ char result_buf[NVLIST_BUFSIZE];
+ int error, i;
/* Create iSCSI port. */
- if (port->p_portal_group) {
+ if (port->p_portal_group || port->p_ioctl_port) {
bzero(&req, sizeof(req));
- strlcpy(req.driver, "iscsi", sizeof(req.driver));
req.reqtype = CTL_REQ_CREATE;
- req.num_args = 5;
- TAILQ_FOREACH(o, &pg->pg_options, o_next)
- req.num_args++;
- req.args = malloc(req.num_args * sizeof(*req.args));
- if (req.args == NULL)
- log_err(1, "malloc");
- n = 0;
- req.args[n].namelen = sizeof("port_id");
- req.args[n].name = __DECONST(char *, "port_id");
- req.args[n].vallen = sizeof(port->p_ctl_port);
- req.args[n].value = &port->p_ctl_port;
- req.args[n++].flags = CTL_BEARG_WR;
- str_arg(&req.args[n++], "cfiscsi_target", targ->t_name);
- snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
- str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr);
- if (targ->t_alias)
- str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias);
- str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name);
- TAILQ_FOREACH(o, &pg->pg_options, o_next)
- str_arg(&req.args[n++], o->o_name, o->o_value);
- req.num_args = n;
+
+ if (port->p_portal_group) {
+ strlcpy(req.driver, "iscsi", sizeof(req.driver));
+ req.args_nvl = nvlist_create(0);
+ nvlist_add_string(req.args_nvl, "cfiscsi_target",
+ targ->t_name);
+ nvlist_add_string(req.args_nvl,
+ "ctld_portal_group_name", pg->pg_name);
+ nvlist_add_stringf(req.args_nvl,
+ "cfiscsi_portal_group_tag", "%u", pg->pg_tag);
+
+ if (targ->t_alias) {
+ nvlist_add_string(req.args_nvl,
+ "cfiscsi_target_alias", targ->t_alias);
+ }
+
+ TAILQ_FOREACH(o, &pg->pg_options, o_next)
+ nvlist_add_string(req.args_nvl, o->o_name,
+ o->o_value);
+ }
+
+ if (port->p_ioctl_port) {
+ strlcpy(req.driver, "ioctl", sizeof(req.driver));
+ req.args_nvl = nvlist_create(0);
+ nvlist_add_stringf(req.args_nvl, "pp", "%d",
+ port->p_ioctl_pp);
+ nvlist_add_stringf(req.args_nvl, "vp", "%d",
+ port->p_ioctl_vp);
+ }
+
+ req.args = nvlist_pack(req.args_nvl, &req.args_len);
+ if (req.args == NULL) {
+ log_warn("error packing nvlist");
+ return (1);
+ }
+
+ req.result = result_buf;
+ req.result_len = sizeof(result_buf);
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
- free(req.args);
+ nvlist_destroy(req.args_nvl);
+
if (error != 0) {
log_warn("error issuing CTL_PORT_REQ ioctl");
return (1);
@@ -1033,6 +1037,15 @@ kernel_port_add(struct port *port)
req.status);
return (1);
}
+
+ req.result_nvl = nvlist_unpack(result_buf, req.result_len, 0);
+ if (req.result_nvl == NULL) {
+ log_warnx("error unpacking result nvlist");
+ return (1);
+ }
+
+ port->p_ctl_port = nvlist_get_number(req.result_nvl, "port_id");
+ nvlist_destroy(req.result_nvl);
} else if (port->p_pport) {
port->p_ctl_port = port->p_pport->pp_ctl_port;
@@ -1116,7 +1129,6 @@ kernel_port_remove(struct port *port)
struct ctl_port_entry entry;
struct ctl_lun_map lm;
struct ctl_req req;
- char tagstr[16];
struct target *targ = port->p_target;
struct portal_group *pg = port->p_portal_group;
int error;
@@ -1130,20 +1142,35 @@ kernel_port_remove(struct port *port)
return (-1);
}
- /* Remove iSCSI port. */
- if (port->p_portal_group) {
+ /* Remove iSCSI or ioctl port. */
+ if (port->p_portal_group || port->p_ioctl_port) {
bzero(&req, sizeof(req));
- strlcpy(req.driver, "iscsi", sizeof(req.driver));
+ strlcpy(req.driver, port->p_ioctl_port ? "ioctl" : "iscsi",
+ sizeof(req.driver));
req.reqtype = CTL_REQ_REMOVE;
- req.num_args = 2;
- req.args = malloc(req.num_args * sizeof(*req.args));
- if (req.args == NULL)
- log_err(1, "malloc");
- str_arg(&req.args[0], "cfiscsi_target", targ->t_name);
- snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
- str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr);
+ req.args_nvl = nvlist_create(0);
+ if (req.args_nvl == NULL)
+ log_err(1, "nvlist_create");
+
+ if (port->p_ioctl_port)
+ nvlist_add_stringf(req.args_nvl, "port_id", "%d",
+ port->p_ctl_port);
+ else {
+ nvlist_add_string(req.args_nvl, "cfiscsi_target",
+ targ->t_name);
+ nvlist_add_stringf(req.args_nvl,
+ "cfiscsi_portal_group_tag", "%u", pg->pg_tag);
+ }
+
+ req.args = nvlist_pack(req.args_nvl, &req.args_len);
+ if (req.args == NULL) {
+ log_warn("error packing nvlist");
+ return (1);
+ }
+
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
- free(req.args);
+ nvlist_destroy(req.args_nvl);
+
if (error != 0) {
log_warn("error issuing CTL_PORT_REQ ioctl");
return (1);
diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y
index 3d8f2fc92313..c9478405599a 100644
--- a/usr.sbin/ctld/parse.y
+++ b/usr.sbin/ctld/parse.y
@@ -768,28 +768,41 @@ target_port: PORT STR
{
struct pport *pp;
struct port *tp;
-
- pp = pport_find(conf, $2);
- if (pp == NULL) {
- log_warnx("unknown port \"%s\" for target \"%s\"",
- $2, target->t_name);
- free($2);
- return (1);
- }
- if (!TAILQ_EMPTY(&pp->pp_ports)) {
- log_warnx("can't link port \"%s\" to target \"%s\", "
- "port already linked to some target",
- $2, target->t_name);
- free($2);
- return (1);
- }
- tp = port_new_pp(conf, target, pp);
- if (tp == NULL) {
- log_warnx("can't link port \"%s\" to target \"%s\"",
- $2, target->t_name);
- free($2);
- return (1);
+ int ret, i_pp, i_vp = 0;
+
+ ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp);
+ if (ret > 0) {
+ tp = port_new_ioctl(conf, target, i_pp, i_vp);
+ if (tp == NULL) {
+ log_warnx("can't create new ioctl port for "
+ "target \"%s\"", target->t_name);
+ free($2);
+ return (1);
+ }
+ } else {
+ pp = pport_find(conf, $2);
+ if (pp == NULL) {
+ log_warnx("unknown port \"%s\" for target \"%s\"",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ if (!TAILQ_EMPTY(&pp->pp_ports)) {
+ log_warnx("can't link port \"%s\" to target \"%s\", "
+ "port already linked to some target",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
+ tp = port_new_pp(conf, target, pp);
+ if (tp == NULL) {
+ log_warnx("can't link port \"%s\" to target \"%s\"",
+ $2, target->t_name);
+ free($2);
+ return (1);
+ }
}
+
free($2);
}
;
diff --git a/usr.sbin/ctld/uclparse.c b/usr.sbin/ctld/uclparse.c
index f26e84903e15..3d39da42569e 100644
--- a/usr.sbin/ctld/uclparse.c
+++ b/usr.sbin/ctld/uclparse.c
@@ -758,6 +758,19 @@ uclparse_target(const char *name, const ucl_object_t *top)
struct pport *pp;
struct port *tp;
const char *value = ucl_object_tostring(obj);
+ int ret, i_pp, i_vp = 0;
+
+ ret = sscanf(value, "ioctl/%d/%d", &i_pp, &i_vp);
+ if (ret > 0) {
+ tp = port_new_ioctl(conf, target, i_pp, i_vp);
+ if (tp == NULL) {
+ log_warnx("can't create new ioctl port "
+ "for target \"%s\"", target->t_name);
+ return (1);
+ }
+
+ return (0);
+ }
pp = pport_find(conf, value);
if (pp == NULL) {