Use pass through interface for sendtargets (take4) Currenly offload cards like bnx2i...
[open-iscsi.git] / usr / iscsid.c
blobbce80c802012b3002e6e8aa52c2e4e75d2d7d46e
1 /*
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.
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 <sys/mman.h>
31 #include <sys/utsname.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
37 #include "iscsid.h"
38 #include "mgmt_ipc.h"
39 #include "event_poll.h"
40 #include "iscsi_ipc.h"
41 #include "log.h"
42 #include "iscsi_util.h"
43 #include "initiator.h"
44 #include "transport.h"
45 #include "idbm.h"
46 #include "version.h"
47 #include "iscsi_sysfs.h"
48 #include "iface.h"
49 #include "session_info.h"
50 #include "sysdeps.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";
59 static pid_t log_pid;
60 static gid_t gid;
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'},
74 {NULL, 0, NULL, 0},
77 static void usage(int status)
79 if (status != 0)
80 fprintf(stderr, "Try `%s --help' for more information.\n",
81 program_name);
82 else {
83 printf("Usage: %s [OPTION]\n", program_name);
84 printf("\
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\
95 ");
97 exit(status == 0 ? 0 : -1);
100 static void
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;
142 else
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;
150 else
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;
174 else
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;
181 else
182 rec->session.iscsi.InitialR2T = 1;
186 static int sync_session(void *data, struct session_info *info)
188 node_rec_t rec, sysfsrec;
189 iscsiadm_req_t req;
190 iscsiadm_rsp_t rsp;
191 struct iscsi_transport *t;
192 int rc, retries = 0;
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);
199 if (!t)
200 return 0;
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) {
207 uint32_t host_no;
208 int err;
210 host_no = iscsi_sysfs_get_host_no_from_sid(info->sid, &err);
211 if (err) {
212 log_error("Could not get host no from sid %u. Can not "
213 "sync session. Error %d", info->sid, err);
214 return 0;
216 iscsi_sysfs_scan_host(host_no, 0);
217 return 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,
230 &info->iface)) {
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);
234 } else {
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))
258 return -1;
260 * We use the initiator name from sysfs because
261 * the session could have come from our db or ibft or some other
262 * app.
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));
270 retry:
271 rc = iscsid_exec_req(&req, &rsp, 0);
272 if (rc == MGMT_IPC_ERR_ISCSID_NOTCONN && retries < 30) {
273 retries++;
274 sleep(1);
275 goto retry;
277 return 0;
280 static char *iscsid_get_config_file(void)
282 return daemon_config.config_file;
285 static void iscsid_shutdown(void)
287 pid_t pid;
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");
296 log_close(log_pid);
300 static void catch_signal(int signo)
302 log_debug(1, "%d caught signal -%d...", signo, getpid());
303 switch (signo) {
304 case SIGTERM:
305 iscsid_shutdown();
306 exit(0);
307 break;
308 default:
309 break;
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;
333 int ch, longindex;
334 uid_t uid = 0;
335 struct sigaction sa_old;
336 struct sigaction sa_new;
337 int control_fd;
338 pid_t pid;
340 /* do not allow ctrl-c for now... */
341 sa_new.sa_handler = catch_signal;
342 sigemptyset(&sa_new.sa_mask);
343 sa_new.sa_flags = 0;
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,
349 &longindex)) >= 0) {
350 switch (ch) {
351 case 'c':
352 config_file = optarg;
353 break;
354 case 'i':
355 initiatorname_file = optarg;
356 break;
357 case 'f':
358 daemonize = 0;
359 break;
360 case 'd':
361 log_level = atoi(optarg);
362 break;
363 case 'u':
364 uid = strtoul(optarg, NULL, 10);
365 break;
366 case 'g':
367 gid = strtoul(optarg, NULL, 10);
368 break;
369 case 'p':
370 pid_file = optarg;
371 break;
372 case 'v':
373 printf("%s version %s\n", program_name,
374 ISCSI_VERSION_STR);
375 exit(0);
376 case 'h':
377 usage(0);
378 break;
379 default:
380 usage(1);
381 break;
385 /* initialize logger */
386 log_pid = log_init(program_name, DEFAULT_AREA_SIZE,
387 daemonize ? log_do_log_daemon : log_do_log_std, NULL);
388 if (log_pid < 0)
389 exit(1);
391 sysfs_init();
392 if (idbm_init(iscsid_get_config_file)) {
393 log_close(log_pid);
394 exit(1);
397 if (iscsi_sysfs_check_class_version()) {
398 log_close(log_pid);
399 exit(1);
402 umask(0177);
404 mgmt_ipc_fd = -1;
405 control_fd = -1;
406 daemon_config.initiator_name = NULL;
407 daemon_config.initiator_alias = NULL;
409 if ((mgmt_ipc_fd = mgmt_ipc_listen()) < 0) {
410 log_close(log_pid);
411 exit(1);
414 if (daemonize) {
415 char buf[64];
416 int fd;
418 fd = open(pid_file, O_WRONLY|O_CREAT, 0644);
419 if (fd < 0) {
420 log_error("Unable to create pid file");
421 log_close(log_pid);
422 exit(1);
424 pid = fork();
425 if (pid < 0) {
426 log_error("Starting daemon failed");
427 log_close(log_pid);
428 exit(1);
429 } else if (pid) {
430 log_error("iSCSI daemon with pid=%d started!", pid);
431 exit(0);
434 if ((control_fd = ipc->ctldev_open()) < 0) {
435 log_close(log_pid);
436 exit(1);
439 chdir("/");
440 if (lockf(fd, F_TLOCK, 0) < 0) {
441 log_error("Unable to lock pid file");
442 log_close(log_pid);
443 exit(1);
445 ftruncate(fd, 0);
446 sprintf(buf, "%d\n", getpid());
447 write(fd, buf, strlen(buf));
449 daemon_init();
450 } else {
451 if ((control_fd = ipc->ctldev_open()) < 0) {
452 log_close(log_pid);
453 exit(1);
457 if (uid && setuid(uid) < 0)
458 perror("setuid\n");
460 if (gid && setgid(gid) < 0)
461 perror("setgid\n");
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,
467 "InitiatorName");
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,
474 "InitiatorAlias");
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);
487 pid = fork();
488 if (pid == 0) {
489 int nr_found = 0;
490 /* child */
491 iscsi_sysfs_for_each_session(NULL, &nr_found, sync_session);
492 exit(0);
493 } else if (pid < 0) {
494 log_error("Fork failed error %d: existing sessions"
495 " will not be synced", errno);
496 } else
497 reap_inc();
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... */
504 if (oom_adjust())
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...");
510 log_close(log_pid);
511 exit(1);
514 actor_init();
515 event_loop(ipc, control_fd, mgmt_ipc_fd);
517 idbm_terminate();
518 sysfs_cleanup();
519 ipc->ctldev_close();
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);
525 free_initiator();
526 iscsid_shutdown();
527 return 0;