2 * iSCSI Initiator Daemon
4 * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
5 * Copyright (C) 2006 Mike Christie
6 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
8 * maintained by open-iscsi@googlegroups.com
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * See the file COPYING included with this distribution for more details.
31 #include <sys/utsname.h>
32 #include <sys/types.h>
34 #include <sys/types.h>
39 #include "event_poll.h"
40 #include "iscsi_ipc.h"
42 #include "iscsi_util.h"
43 #include "initiator.h"
44 #include "transport.h"
47 #include "iscsi_sysfs.h"
49 #include "session_info.h"
51 #include "discoveryd.h"
52 #include "iscsid_req.h"
54 /* global config info */
55 struct iscsi_daemon_config daemon_config
;
56 struct iscsi_daemon_config
*dconfig
= &daemon_config
;
58 static char program_name
[] = "iscsid";
61 static int daemonize
= 1;
62 static int mgmt_ipc_fd
;
64 static struct option
const long_options
[] = {
65 {"config", required_argument
, NULL
, 'c'},
66 {"initiatorname", required_argument
, NULL
, 'i'},
67 {"foreground", no_argument
, NULL
, 'f'},
68 {"debug", required_argument
, NULL
, 'd'},
69 {"uid", required_argument
, NULL
, 'u'},
70 {"gid", required_argument
, NULL
, 'g'},
71 {"pid", required_argument
, NULL
, 'p'},
72 {"help", no_argument
, NULL
, 'h'},
73 {"version", no_argument
, NULL
, 'v'},
77 static void usage(int status
)
80 fprintf(stderr
, "Try `%s --help' for more information.\n",
83 printf("Usage: %s [OPTION]\n", program_name
);
85 Open-iSCSI initiator daemon.\n\
86 -c, --config=[path] Execute in the config file (" CONFIG_FILE
").\n\
87 -i, --initiatorname=[path] read initiatorname from file (" INITIATOR_NAME_FILE
").\n\
88 -f, --foreground make the program run in the foreground\n\
89 -d, --debug debuglevel print debugging information\n\
90 -u, --uid=uid run as uid, default is current user\n\
91 -g, --gid=gid run as gid, default is current user group\n\
92 -p, --pid=pidfile use pid file (default " PID_FILE
").\n\
93 -h, --help display this help and exit\n\
94 -v, --version display version and exit\n\
97 exit(status
== 0 ? 0 : -1);
101 setup_rec_from_negotiated_values(node_rec_t
*rec
, struct session_info
*info
)
103 struct iscsi_session_operational_config session_conf
;
104 struct iscsi_conn_operational_config conn_conf
;
105 struct iscsi_auth_config auth_conf
;
107 idbm_node_setup_from_conf(rec
);
108 strlcpy(rec
->name
, info
->targetname
, TARGET_NAME_MAXLEN
);
109 rec
->conn
[0].port
= info
->persistent_port
;
110 strlcpy(rec
->conn
[0].address
, info
->persistent_address
, NI_MAXHOST
);
111 memcpy(&rec
->iface
, &info
->iface
, sizeof(struct iface_rec
));
112 rec
->tpgt
= info
->tpgt
;
113 iface_copy(&rec
->iface
, &info
->iface
);
115 iscsi_sysfs_get_negotiated_session_conf(info
->sid
, &session_conf
);
116 iscsi_sysfs_get_negotiated_conn_conf(info
->sid
, &conn_conf
);
117 iscsi_sysfs_get_auth_conf(info
->sid
, &auth_conf
);
119 if (strlen(auth_conf
.username
))
120 strcpy(rec
->session
.auth
.username
, auth_conf
.username
);
122 if (strlen(auth_conf
.username_in
))
123 strcpy(rec
->session
.auth
.username_in
, auth_conf
.username_in
);
125 if (strlen((char *)auth_conf
.password
)) {
126 strcpy((char *)rec
->session
.auth
.password
,
127 (char *)auth_conf
.password
);
128 rec
->session
.auth
.password_length
= auth_conf
.password_length
;
131 if (strlen((char *)auth_conf
.password_in
)) {
132 strcpy((char *)rec
->session
.auth
.password_in
,
133 (char *)auth_conf
.password_in
);
134 rec
->session
.auth
.password_in_length
=
135 auth_conf
.password_in_length
;
138 if (is_valid_operational_value(conn_conf
.HeaderDigest
)) {
139 if (conn_conf
.HeaderDigest
)
140 rec
->conn
[0].iscsi
.HeaderDigest
=
141 CONFIG_DIGEST_PREFER_ON
;
143 rec
->conn
[0].iscsi
.HeaderDigest
=
144 CONFIG_DIGEST_PREFER_OFF
;
147 if (is_valid_operational_value(conn_conf
.DataDigest
)) {
148 if (conn_conf
.DataDigest
)
149 rec
->conn
[0].iscsi
.DataDigest
= CONFIG_DIGEST_PREFER_ON
;
151 rec
->conn
[0].iscsi
.DataDigest
=
152 CONFIG_DIGEST_PREFER_OFF
;
155 if (is_valid_operational_value(conn_conf
.MaxRecvDataSegmentLength
))
156 rec
->conn
[0].iscsi
.MaxRecvDataSegmentLength
=
157 conn_conf
.MaxRecvDataSegmentLength
;
159 if (is_valid_operational_value(conn_conf
.MaxXmitDataSegmentLength
))
160 rec
->conn
[0].iscsi
.MaxXmitDataSegmentLength
=
161 conn_conf
.MaxXmitDataSegmentLength
;
163 if (is_valid_operational_value(session_conf
.FirstBurstLength
))
164 rec
->session
.iscsi
.FirstBurstLength
=
165 session_conf
.FirstBurstLength
;
167 if (is_valid_operational_value(session_conf
.MaxBurstLength
))
168 rec
->session
.iscsi
.MaxBurstLength
=
169 session_conf
.MaxBurstLength
;
171 if (is_valid_operational_value(session_conf
.ImmediateData
)) {
172 if (session_conf
.ImmediateData
)
173 rec
->session
.iscsi
.ImmediateData
= 1;
175 rec
->session
.iscsi
.ImmediateData
= 0;
178 if (is_valid_operational_value(session_conf
.InitialR2T
)) {
179 if (session_conf
.InitialR2T
)
180 rec
->session
.iscsi
.InitialR2T
= 0;
182 rec
->session
.iscsi
.InitialR2T
= 1;
186 static int sync_session(void *data
, struct session_info
*info
)
188 node_rec_t rec
, sysfsrec
;
191 struct iscsi_transport
*t
;
194 log_debug(7, "sync session [%d][%s,%s.%d][%s]\n", info
->sid
,
195 info
->targetname
, info
->persistent_address
,
196 info
->port
, info
->iface
.hwaddress
);
198 t
= iscsi_sysfs_get_transport_by_sid(info
->sid
);
203 * Just rescan the device in case this is the first startup.
204 * (TODO: should do this async and check for state).
206 if (t
->caps
& CAP_FW_DB
) {
210 host_no
= iscsi_sysfs_get_host_no_from_sid(info
->sid
, &err
);
212 log_error("Could not get host no from sid %u. Can not "
213 "sync session. Error %d", info
->sid
, err
);
216 iscsi_sysfs_scan_host(host_no
, 0);
220 memset(&rec
, 0, sizeof(node_rec_t
));
222 * We might get the local ip address for software. We do not
223 * want to try and bind a session by ip though.
225 if (!t
->template->set_host_ip
)
226 memset(info
->iface
.ipaddress
, 0, sizeof(info
->iface
.ipaddress
));
228 if (idbm_rec_read(&rec
, info
->targetname
, info
->tpgt
,
229 info
->persistent_address
, info
->persistent_port
,
231 log_warning("Could not read data from db. Using default and "
232 "currently negotiated values\n");
233 setup_rec_from_negotiated_values(&rec
, info
);
236 * we have a valid record and iface so lets merge
237 * the values from them and sysfs to try and get
238 * the most uptodate values.
240 * Currenlty that means we will use the CHAP, target, portal
241 * and iface values from sysfs and use timer, queue depth,
242 * and segment length values from the record.
244 memset(&sysfsrec
, 0, sizeof(node_rec_t
));
245 setup_rec_from_negotiated_values(&sysfsrec
, info
);
247 * target, portal and iface name values have to be the same
248 * or we would not have found the record, so just copy
249 * CHAP and iface settings.
251 memcpy(&rec
.session
.auth
, &sysfsrec
.session
.auth
,
252 sizeof(struct iscsi_auth_config
));
253 memcpy(&rec
.iface
, &info
->iface
, sizeof(rec
.iface
));
256 /* multiple drivers could be connected to the same portal */
257 if (!iscsi_match_session(&rec
, info
))
260 * We use the initiator name from sysfs because
261 * the session could have come from our db or ibft or some other
264 strcpy(rec
.iface
.iname
, info
->iface
.iname
);
265 memset(&req
, 0, sizeof(req
));
266 req
.command
= MGMT_IPC_SESSION_SYNC
;
267 req
.u
.session
.sid
= info
->sid
;
268 memcpy(&req
.u
.session
.rec
, &rec
, sizeof(node_rec_t
));
271 rc
= iscsid_exec_req(&req
, &rsp
, 0);
272 if (rc
== MGMT_IPC_ERR_ISCSID_NOTCONN
&& retries
< 30) {
280 static char *iscsid_get_config_file(void)
282 return daemon_config
.config_file
;
285 static void iscsid_shutdown(void)
289 killpg(gid
, SIGTERM
);
290 while ((pid
= waitpid(0, NULL
, 0) > 0))
291 log_debug(7, "cleaned up pid %d", pid
);
293 log_warning("iscsid shutting down.");
294 if (daemonize
&& log_pid
>= 0) {
295 log_debug(1, "daemon stopping");
300 static void catch_signal(int signo
)
302 log_debug(1, "%d caught signal -%d...", signo
, getpid());
313 static void missing_iname_warn(char *initiatorname_file
)
315 log_error("Warning: InitiatorName file %s does not exist or does not "
316 "contain a properly formated InitiatorName. If using "
317 "software iscsi (iscsi_tcp or ib_iser) or partial offload "
318 "(bnx2i or cxgbi iscsi), you may not be able to log "
319 "into or discover targets. Please create a file %s that "
320 "contains a sting with the format: InitiatorName="
321 "iqn.yyyy-mm.<reversed domain name>[:identifier].\n\n"
322 "Example: InitiatorName=iqn.2001-04.com.redhat:fc6.\n"
323 "If using hardware iscsi like qla4xxx this message can be "
324 "ignored.\n", initiatorname_file
, initiatorname_file
);
327 int main(int argc
, char *argv
[])
329 struct utsname host_info
; /* will use to compound initiator alias */
330 char *config_file
= CONFIG_FILE
;
331 char *initiatorname_file
= INITIATOR_NAME_FILE
;
332 char *pid_file
= PID_FILE
;
335 struct sigaction sa_old
;
336 struct sigaction sa_new
;
340 /* do not allow ctrl-c for now... */
341 sa_new
.sa_handler
= catch_signal
;
342 sigemptyset(&sa_new
.sa_mask
);
344 sigaction(SIGINT
, &sa_new
, &sa_old
);
345 sigaction(SIGPIPE
, &sa_new
, &sa_old
);
346 sigaction(SIGTERM
, &sa_new
, &sa_old
);
348 while ((ch
= getopt_long(argc
, argv
, "c:i:fd:u:g:p:vh", long_options
,
352 config_file
= optarg
;
355 initiatorname_file
= optarg
;
361 log_level
= atoi(optarg
);
364 uid
= strtoul(optarg
, NULL
, 10);
367 gid
= strtoul(optarg
, NULL
, 10);
373 printf("%s version %s\n", program_name
,
385 /* initialize logger */
386 log_pid
= log_init(program_name
, DEFAULT_AREA_SIZE
,
387 daemonize
? log_do_log_daemon
: log_do_log_std
, NULL
);
392 if (idbm_init(iscsid_get_config_file
)) {
397 if (iscsi_sysfs_check_class_version()) {
406 daemon_config
.initiator_name
= NULL
;
407 daemon_config
.initiator_alias
= NULL
;
409 if ((mgmt_ipc_fd
= mgmt_ipc_listen()) < 0) {
418 fd
= open(pid_file
, O_WRONLY
|O_CREAT
, 0644);
420 log_error("Unable to create pid file");
426 log_error("Starting daemon failed");
430 log_error("iSCSI daemon with pid=%d started!", pid
);
434 if ((control_fd
= ipc
->ctldev_open()) < 0) {
440 if (lockf(fd
, F_TLOCK
, 0) < 0) {
441 log_error("Unable to lock pid file");
446 sprintf(buf
, "%d\n", getpid());
447 write(fd
, buf
, strlen(buf
));
451 if ((control_fd
= ipc
->ctldev_open()) < 0) {
457 if (uid
&& setuid(uid
) < 0)
460 if (gid
&& setgid(gid
) < 0)
463 memset(&daemon_config
, 0, sizeof (daemon_config
));
464 daemon_config
.pid_file
= pid_file
;
465 daemon_config
.config_file
= config_file
;
466 daemon_config
.initiator_name
= cfg_get_string_param(initiatorname_file
,
468 if (daemon_config
.initiator_name
== NULL
)
469 missing_iname_warn(initiatorname_file
);
471 /* optional InitiatorAlias */
472 daemon_config
.initiator_alias
=
473 cfg_get_string_param(initiatorname_file
,
475 if (!daemon_config
.initiator_alias
) {
476 memset(&host_info
, 0, sizeof (host_info
));
477 if (uname(&host_info
) >= 0) {
478 daemon_config
.initiator_alias
=
479 strdup(host_info
.nodename
);
483 log_debug(1, "InitiatorName=%s", daemon_config
.initiator_name
?
484 daemon_config
.initiator_name
: "NOT SET");
485 log_debug(1, "InitiatorAlias=%s", daemon_config
.initiator_alias
);
491 iscsi_sysfs_for_each_session(NULL
, &nr_found
, sync_session
);
493 } else if (pid
< 0) {
494 log_error("Fork failed error %d: existing sessions"
495 " will not be synced", errno
);
499 iscsi_initiator_init();
500 increase_max_files();
501 discoveryd_start(daemon_config
.initiator_name
);
503 /* oom-killer will not kill us at the night... */
505 log_debug(1, "can not adjust oom-killer's pardon");
507 /* we don't want our active sessions to be paged out... */
508 if (mlockall(MCL_CURRENT
| MCL_FUTURE
)) {
509 log_error("failed to mlockall, exiting...");
515 event_loop(ipc
, control_fd
, mgmt_ipc_fd
);
520 mgmt_ipc_close(mgmt_ipc_fd
);
521 if (daemon_config
.initiator_name
)
522 free(daemon_config
.initiator_name
);
523 if (daemon_config
.initiator_alias
)
524 free(daemon_config
.initiator_alias
);