iscsi boot: check for nic/tgt array limits
[open-iscsi.git] / utils / fwparam_ibft / fwparam_ibft_sysfs.c
blob98e29a686d4cd2637fb54a19b06aee860ae9b52f
1 /*
2 * Copyright (C) IBM Corporation. 2007
3 * Author: Konrad Rzeszutek <konradr@linux.vnet.ibm.com>
4 * Copyright (C) Red Hat, Inc. All rights reserved. 2008
5 * Copyright (C) Mike Christie 2008
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #define _XOPEN_SOURCE 500
22 #include <ftw.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <dirent.h>
30 #include <sys/types.h>
32 #include "sysfs.h"
33 #include "fw_context.h"
34 #include "fwparam.h"
35 #include "sysdeps.h"
37 #define IBFT_MAX 255
38 #define IBFT_SYSFS_ROOT "/sys/firmware/ibft/"
39 #define IBFT_SUBSYS "ibft"
41 static char *target_list[IBFT_MAX];
42 static char *nic_list[IBFT_MAX];
43 static int nic_cnt;
44 static int tgt_cnt;
46 static int file_exist(const char *file)
48 struct stat bootpath_stat;
50 return !stat(file, &bootpath_stat);
54 * Finds the etherrnetX and targetX under the sysfs directory.
56 static int find_sysfs_dirs(const char *fpath, const struct stat *sb,
57 int tflag, struct FTW *ftw)
59 if (tflag == FTW_D && (strstr(fpath + ftw->base, "target"))) {
60 if (tgt_cnt == IBFT_MAX) {
61 printf("Too many targets found in IBFT data."
62 "Max number of targets %d\n", IBFT_MAX);
63 return 0;
65 target_list[tgt_cnt++] = strdup(strstr(fpath, "target"));
68 if (tflag == FTW_D && (strstr(fpath + ftw->base, "ethernet"))) {
69 if (nic_cnt == IBFT_MAX) {
70 printf("Too many nics found in IBFT data."
71 "Max number of nics %d\n", IBFT_MAX);
72 return 0;
74 nic_list[nic_cnt++] = strdup(strstr(fpath, "ethernet"));
77 return 0;
80 static int get_iface_from_device(char *id, struct boot_context *context)
82 char dev_dir[FILENAMESZ];
83 int rc = ENODEV;
84 DIR *dirfd;
85 struct dirent *dent;
87 memset(dev_dir, 0, FILENAMESZ);
88 snprintf(dev_dir, FILENAMESZ, IBFT_SYSFS_ROOT"/%s/device", id);
90 if (!file_exist(dev_dir))
91 return 0;
93 dirfd = opendir(dev_dir);
94 if (!dirfd)
95 return errno;
97 while ((dent = readdir(dirfd))) {
98 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..") ||
99 strncmp(dent->d_name, "net", 3))
100 continue;
102 if (!strncmp(dent->d_name, "net:", 4)) {
103 if ((strlen(dent->d_name) - 4) >
104 (sizeof(context->iface) - 1)) {
105 rc = EINVAL;
106 printf("Net device %s too big for iface "
107 "buffer.\n", dent->d_name);
108 break;
111 if (sscanf(dent->d_name, "net:%s", context->iface) != 1)
112 rc = EINVAL;
113 rc = 0;
114 break;
115 } else {
116 printf("Could not read ethernet to net link\n.");
117 rc = EOPNOTSUPP;
118 break;
122 closedir(dirfd);
124 if (rc != ENODEV)
125 return rc;
127 /* If not found try again with newer kernel networkdev sysfs layout */
128 strlcat(dev_dir, "/net", FILENAMESZ);
130 if (!file_exist(dev_dir))
131 return rc;
133 dirfd = opendir(dev_dir);
134 if (!dirfd)
135 return errno;
137 while ((dent = readdir(dirfd))) {
138 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
139 continue;
141 /* Take the first "regular" directory entry */
142 if (strlen(dent->d_name) > (sizeof(context->iface) - 1)) {
143 rc = EINVAL;
144 printf("Net device %s too big for iface buffer.\n",
145 dent->d_name);
146 break;
149 strcpy(context->iface, dent->d_name);
150 rc = 0;
151 break;
154 closedir(dirfd);
156 return rc;
160 * Routines to fill in the context values.
162 static int fill_nic_context(char *id, struct boot_context *context)
164 int rc;
166 rc = sysfs_get_str(id, IBFT_SUBSYS, "mac", context->mac,
167 sizeof(context->mac));
168 if (rc)
169 return rc;
171 rc = get_iface_from_device(id, context);
172 if (rc)
173 return rc;
175 sysfs_get_str(id, IBFT_SUBSYS, "ip-addr", context->ipaddr,
176 sizeof(context->ipaddr));
177 sysfs_get_str(id, IBFT_SUBSYS, "vlan", context->vlan,
178 sizeof(context->vlan));
179 sysfs_get_str(id, IBFT_SUBSYS, "subnet-mask", context->mask,
180 sizeof(context->mask));
181 sysfs_get_str(id, IBFT_SUBSYS, "gateway", context->gateway,
182 sizeof(context->gateway));
183 sysfs_get_str(id, IBFT_SUBSYS, "primary-dns", context->primary_dns,
184 sizeof(context->primary_dns));
185 sysfs_get_str(id, IBFT_SUBSYS, "secondary-dns", context->secondary_dns,
186 sizeof(context->secondary_dns));
187 sysfs_get_str(id, IBFT_SUBSYS, "dhcp", context->dhcp,
188 sizeof(context->dhcp));
189 return 0;
192 static void fill_initiator_context(struct boot_context *context)
194 sysfs_get_str("initiator", IBFT_SUBSYS, "initiator-name",
195 context->initiatorname,
196 sizeof(context->initiatorname));
197 sysfs_get_str("initiator", IBFT_SUBSYS, "isid", context->isid,
198 sizeof(context->isid));
200 static int fill_tgt_context(char *id, struct boot_context *context)
202 int rc;
204 rc = sysfs_get_str(id, IBFT_SUBSYS, "target-name", context->targetname,
205 sizeof(context->targetname));
206 if (rc)
207 return rc;
209 rc = sysfs_get_str(id, IBFT_SUBSYS, "ip-addr", context->target_ipaddr,
210 sizeof(context->target_ipaddr));
211 if (rc)
212 return rc;
215 * We can live without the rest of they do not exist. If we
216 * failed to get them we will figure it out when we login.
218 if (sysfs_get_int(id, IBFT_SUBSYS, "port", &context->target_port))
219 context->target_port = ISCSI_LISTEN_PORT;
221 sysfs_get_str(id, IBFT_SUBSYS, "lun", context->lun,
222 sizeof(context->lun));
223 sysfs_get_str(id, IBFT_SUBSYS, "chap-name", context->chap_name,
224 sizeof(context->chap_name));
225 sysfs_get_str(id, IBFT_SUBSYS, "chap-secret",
226 context->chap_password,
227 sizeof(context->chap_password));
228 sysfs_get_str(id, IBFT_SUBSYS, "rev-chap-name",
229 context->chap_name_in,
230 sizeof(context->chap_name_in));
231 sysfs_get_str(id, IBFT_SUBSYS, "rev-chap-name-secret",
232 context->chap_password_in,
233 sizeof(context->chap_password_in));
234 return 0;
237 #define IBFT_SYSFS_FLAG_FW_SEL_BOOT 2
239 static int find_boot_flag(char *list[], ssize_t size, int *boot_idx)
241 int rc = ENODEV;
242 int i, flag = 0;
244 for (i = 0; i < size; i++, flag = -1) {
245 rc = sysfs_get_int(list[i], IBFT_SUBSYS, "flags", &flag);
246 if (rc)
247 continue;
249 if (flag & IBFT_SYSFS_FLAG_FW_SEL_BOOT) {
250 *boot_idx = i;
251 rc = 0;
252 break;
254 rc = ENODEV;
255 flag = 0;
259 return rc;
262 static void deallocate_lists(void)
264 int i;
266 for (i = 0; i < nic_cnt; i++)
267 free(nic_list[i]);
269 nic_cnt = 0;
270 for (i = 0; i < tgt_cnt; i++)
271 free(target_list[i]);
273 tgt_cnt = 0;
277 int fwparam_ibft_sysfs_boot_info(struct boot_context *context)
279 char initiator_dir[FILENAMESZ];
280 int rc = 1;
281 int nic_idx = -1, tgt_idx = -1;
283 memset(&initiator_dir, 0 , FILENAMESZ);
284 snprintf(initiator_dir, FILENAMESZ, "%sinitiator",
285 IBFT_SYSFS_ROOT);
287 nic_cnt = 0;
288 tgt_cnt = 0;
289 if (file_exist(initiator_dir)) {
290 /* Find the target's and the ethernet's */
291 rc = nftw(IBFT_SYSFS_ROOT, find_sysfs_dirs, 20, 1);
293 /* Find wihch target and which ethernet have
294 the boot flag set. */
295 rc = find_boot_flag(nic_list, nic_cnt, &nic_idx);
296 if (rc)
297 goto free;
299 rc = find_boot_flag(target_list, tgt_cnt, &tgt_idx);
300 if (rc)
301 goto free;
303 /* Fill in the context values */
304 rc = fill_nic_context(nic_list[nic_idx], context);
305 rc |= fill_tgt_context(target_list[tgt_idx], context);
306 fill_initiator_context(context);
308 free:
309 deallocate_lists();
310 return rc;
313 int fwparam_ibft_sysfs_get_targets(struct list_head *list)
315 struct boot_context *context;
316 int rc = 0, i, nic_idx, nic;
317 char initiator_dir[FILENAMESZ];
319 memset(&initiator_dir, 0 , FILENAMESZ);
320 snprintf(initiator_dir, FILENAMESZ, "%sinitiator",
321 IBFT_SYSFS_ROOT);
323 if (!file_exist(initiator_dir))
324 return ENODEV;
326 nic_cnt = 0;
327 tgt_cnt = 0;
329 /* Find the target's and the ethernet's */
330 nftw(IBFT_SYSFS_ROOT, find_sysfs_dirs, 20, 1);
331 for (i = 0; i < tgt_cnt; i++) {
332 context = calloc(1, sizeof(*context));
333 if (!context) {
334 rc = ENOMEM;
335 break;
338 rc = fill_tgt_context(target_list[i], context);
339 if (rc)
340 break;
342 rc = sysfs_get_int(target_list[i], IBFT_SUBSYS, "nic-assoc",
343 &nic_idx);
344 if (rc)
345 break;
347 for (nic = 0; nic < nic_cnt; nic++) {
348 int id;
350 rc = sysfs_get_int(nic_list[nic], IBFT_SUBSYS, "index",
351 &id);
352 if (!rc && (id == nic_idx))
353 break;
356 if (nic == nic_cnt) {
357 printf("Invalid nic-assoc of %d. Max id %d.\n",
358 nic_idx, nic_cnt);
359 break;
362 rc = fill_nic_context(nic_list[nic], context);
363 if (rc)
364 break;
366 fill_initiator_context(context);
367 list_add_tail(&context->list, list);
370 if (rc) {
371 if (context)
372 free(context);
373 fw_free_targets(list);
376 deallocate_lists();
377 return rc;