common/recipes-core/ncsi-util/files/ncsi-util.c (662 lines of code) (raw):

/* * ncsi-util * * Copyright 2015-present Facebook. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <syslog.h> #include <stdint.h> #include <stddef.h> #include <pthread.h> #include <time.h> #include <unistd.h> #include <getopt.h> #include <sys/socket.h> #include <sys/utsname.h> #include <linux/netlink.h> #include <openbmc/pal.h> #include <openbmc/kv.h> #include <openbmc/ncsi.h> #include <openbmc/nl-wrapper.h> #include <openbmc/pldm.h> #include <openbmc/pldm_base.h> #include <openbmc/pldm_fw_update.h> #include "ncsi-util.h" #include "brcm-ncsi-util.h" #include "nvidia-ncsi-util.h" #ifndef max #define max(a, b) ((a) > (b)) ? (a) : (b) #endif #define MAX_SEND_NL_MSG_RETRY 3 int nl_conf = -1; // default value indicating auto-detection enum { STATE_IDLE=0, STATE_LEARN_COMPONENTS, STATE_READY_XFER, STATE_DOWNLOAD, STATE_VERIFY, STATE_APPLY, STATE_ACTIVATE, }; // ncsi-util API for communicating with kernel // // Take a NCSI_NL_MSG_T buffer, send it to kernel, and returns reply from kernel // in NCSI_NL_RSP_T format NCSI_NL_RSP_T * (*send_nl_msg)(NCSI_NL_MSG_T *nl_msg); // Sending data to kernel via NETLINK_USER message type // (legacy mode, only used in kernel v4.1) static NCSI_NL_RSP_T * send_nl_msg_netlink_user(NCSI_NL_MSG_T *nl_msg) { int sock_fd, ret; struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; struct msghdr msg; int req_msg_size = offsetof(NCSI_NL_MSG_T, msg_payload) + nl_msg->payload_length; int msg_size = max(sizeof(NCSI_NL_RSP_T), req_msg_size); /* msg response from kernel */ NCSI_NL_RSP_T *rcv_buf, *ret_buf = NULL; /* open NETLINK socket to send message to kernel */ sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); if (sock_fd < 0) { printf("Error: failed to allocate socket\n"); return NULL; } memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); /* self pid */ bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; /* For Linux Kernel */ dest_addr.nl_groups = 0; /* unicast */ #ifdef DEBUG /* debug printfs */ printf("msg_size=%d, NLMSG_SPACE(msg_size)=%d\n", msg_size, NLMSG_SPACE(msg_size)); #endif nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(msg_size)); if (!nlh) { printf("Error, failed to allocate message buffer (%d)\n", NLMSG_SPACE(msg_size)); goto close_and_exit; } memset(nlh, 0, NLMSG_SPACE(msg_size)); nlh->nlmsg_len = NLMSG_SPACE(req_msg_size); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; /* the actual NC-SI command from user */ memcpy(NLMSG_DATA(nlh), nl_msg, req_msg_size); iov.iov_base = (void *)nlh; iov.iov_len = nlh->nlmsg_len; memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = sendmsg(sock_fd, &msg, 0); if (ret == -1) { printf("Sending NC-SI command error: errno=%d\n", errno); goto free_and_exit; } /* Read message from kernel */ iov.iov_len = NLMSG_SPACE(msg_size); recvmsg(sock_fd, &msg, 0); rcv_buf = (NCSI_NL_RSP_T *)NLMSG_DATA(nlh); unsigned int response_size = sizeof(NCSI_NL_RSP_HDR_T) + rcv_buf->hdr.payload_length; ret_buf = (NCSI_NL_RSP_T *)malloc(response_size); if (!ret_buf) { printf("Error, failed to allocate response buffer (%d)\n", response_size); goto free_and_exit; } memcpy(ret_buf, rcv_buf, response_size); free_and_exit: free(nlh); close_and_exit: close(sock_fd); return ret_buf; } // Determine NC-SI netlink send method static void determine_nl_method() { struct utsname unamebuf; // netlink config - auto detection if (nl_conf == -1) { int ret = uname(&unamebuf); if (!ret) { if (!strcmp(unamebuf.release, "4.1.51")) nl_conf = 0; else nl_conf = 1; } } if (nl_conf == 0) send_nl_msg = send_nl_msg_netlink_user; else send_nl_msg = send_nl_msg_libnl; } // Initialize util arguments static void ncsi_util_init_args(ncsi_util_args_t *util_args) { memset(util_args, 0, sizeof(*util_args)); } // Set NC-SI packet netdev // - If user has specified one, use it; // - Otherwise, if a default device is provided, use it; // - Otherwise, do nothing. void ncsi_util_set_pkt_dev(NCSI_NL_MSG_T *nl_msg, const ncsi_util_args_t *util_args, const char *dflt_dev_name) { const char *dev_name; if (util_args->dev_name) dev_name = util_args->dev_name; else if (dflt_dev_name) dev_name = dflt_dev_name; else return; strncpy(nl_msg->dev_name, dev_name, sizeof(nl_msg->dev_name)); nl_msg->dev_name[sizeof(nl_msg->dev_name)-1] = '\0'; } // Initialize NC-SI netlink message by util arguments void ncsi_util_init_pkt_by_args(NCSI_NL_MSG_T *nl_msg, const ncsi_util_args_t *util_args) { ncsi_util_set_pkt_dev(nl_msg, util_args, NCSI_UTIL_DEFAULT_DEV_NAME); nl_msg->channel_id = util_args->channel_id; } // Print progress. It return the printed percentage unsigned long ncsi_util_print_progress(unsigned long cur, unsigned long total, unsigned long last_percent, const char *prefix_str) { unsigned long percent = ((unsigned long long)cur * 100) / total; /* Only print when percentage changes */ if (cur && percent == last_percent) return last_percent; printf("\r%s %10lu/%lu (%lu%%) ", prefix_str, cur, total, percent); fflush(stdout); if (percent == 100) printf("\n"); return percent; } static void print_pldm_resp_raw(NCSI_NL_RSP_T *nl_resp) { int i; printf("PLDM Payload\n"); for (i = 8; i < nl_resp->hdr.payload_length; ++i) printf("0x%x ", nl_resp->msg_payload[i]); printf("\n"); } static void print_pldm_cmd_status(NCSI_NL_RSP_T *nl_resp) { // Debug code to print complete NC-SI payload // print_ncsi_resp(nl_resp); if (nl_resp->hdr.cmd == NCSI_PLDM_REQUEST) { printf("PLDM Completion Code = 0x%x (%s)\n", ncsiDecodePldmCompCode(nl_resp), pldm_fw_cmd_cc_to_name(ncsiDecodePldmCompCode(nl_resp))); } } // sends nl_msg containing PLDM command across NCSI interface and // and returns response // Returns -1 if error occurs static int sendPldmCmdAndCheckResp(NCSI_NL_MSG_T *nl_msg) { NCSI_NL_RSP_T *nl_resp; int ret = 0; if (!nl_msg) return -1; nl_resp = send_nl_msg(nl_msg); if (!nl_resp) { return -1; } // debug prints print_pldm_cmd_status(nl_resp); ret = ncsiDecodePldmCompCode(nl_resp); free(nl_resp); return ret; } __attribute__((destructor)) static void reset_ncsi_lock(void) { kv_set("block_ncsi_xmit", "0", 0, 0); } static NCSI_NL_RSP_T * send_nl_msg_retry(NCSI_NL_MSG_T *nl_msg) { int retry = 0; NCSI_NL_RSP_T *nl_resp = NULL; while (retry <= MAX_SEND_NL_MSG_RETRY) { nl_resp = send_nl_msg(nl_msg); if (nl_resp != NULL) { break; } retry++; } return nl_resp; } static int pldm_update_fw(char *path, int pldm_bufsize, uint8_t ch) { #define SLEEP_TIME_MS 20 // wait time per loop in ms NCSI_NL_MSG_T *nl_msg = NULL; NCSI_NL_RSP_T *nl_resp = NULL; pldm_fw_pkg_hdr_t *pkgHdr; pldm_cmd_req pldmReq = {0}; pldm_response *pldmRes = NULL; int pldmCmdStatus = 0; int i = 0, j = 0; int ret = 0; int waitcycle = 0; int waitTOsec = 0; int currnet_state = -1, previous_state = -1; char value[64]; struct timespec ts; #define MAX_WAIT_CYCLE 1000 clock_gettime(CLOCK_MONOTONIC, &ts); snprintf(value, sizeof(value), "%ld", ts.tv_sec + 600); kv_set("block_ncsi_xmit", value, 0, 0); nl_msg = calloc(1, sizeof(NCSI_NL_MSG_T)); if (!nl_msg) { printf("%s, Error: failed nl_msg buffer allocation(%d)\n", __FUNCTION__, sizeof(NCSI_NL_MSG_T)); ret = -1; goto free_exit; } memset(nl_msg, 0, sizeof(NCSI_NL_MSG_T)); pldmRes = calloc(1, sizeof(pldm_response)); if (!pldmRes) { printf("%s, Error: failed pldmRes buffer allocation(%d)\n", __FUNCTION__, sizeof(pldm_response)); ret = -1; goto free_exit; } pkgHdr = pldm_parse_fw_pkg(path); if (!pkgHdr) { ret = -1; goto free_exit; } pldmCreateReqUpdateCmd(pkgHdr, &pldmReq, pldm_bufsize); printf("\n01 PldmRequestUpdateOp: payload_size=%d\n", pldmReq.payload_size); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { goto free_exit; } if (sendPldmCmdAndCheckResp(nl_msg) != CC_SUCCESS) { ret = -1; goto free_exit; } for (i=0; i<pkgHdr->componentImageCnt; ++i) { memset(&pldmReq, 0, sizeof(pldm_cmd_req)); pldmCreatePassComponentTblCmd(pkgHdr, i, &pldmReq); printf("\n02 PldmPassComponentTableOp[%d]: payload_size=%d\n", i, pldmReq.payload_size); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { goto free_exit; } if (sendPldmCmdAndCheckResp(nl_msg) != CC_SUCCESS) { ret = -1; goto free_exit; } } for (i=0; i<pkgHdr->componentImageCnt; ++i) { memset(&pldmReq, 0, sizeof(pldm_cmd_req)); pldmCreateUpdateComponentCmd(pkgHdr, i, &pldmReq); printf("\n03 PldmUpdateComponentOp[%d]: payload_size=%d\n", i, pldmReq.payload_size); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { goto free_exit; } if (sendPldmCmdAndCheckResp(nl_msg) != CC_SUCCESS) { ret = -1; goto free_exit; } } // FW data transfer int loopCount = 0; int idleCnt = 0; int pldmCmd = 0; setPldmTimeout(CMD_UPDATE_COMPONENT, &waitTOsec); while (idleCnt < (waitTOsec * 1000 /SLEEP_TIME_MS) ) { // printf("\n04 QueryPendingNcPldmRequestOp, loop=%d\n", loopCount); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_QUERY_PENDING_NC_PLDM_REQ, 0, NULL); if (ret) { goto free_exit; } nl_resp = send_nl_msg_retry(nl_msg); if (!nl_resp) { ret = -1; goto free_exit; } print_pldm_cmd_status(nl_resp); pldmCmd = ncsiGetPldmCmd(nl_resp, &pldmReq); free(nl_resp); nl_resp = NULL; if (pldmCmd == -1) { // printf("No pending command, loop %d\n", idleCnt); msleep(SLEEP_TIME_MS); // wait some time and try again idleCnt++; continue; } else { idleCnt = 0; } if ( (pldmCmd == CMD_REQUEST_FIRMWARE_DATA) || (pldmCmd == CMD_TRANSFER_COMPLETE) || (pldmCmd == CMD_VERIFY_COMPLETE) || (pldmCmd == CMD_APPLY_COMPLETE)) { setPldmTimeout(pldmCmd, &waitTOsec); loopCount++; waitcycle = 0; pldmCmdStatus = pldmFwUpdateCmdHandler(pkgHdr, &pldmReq, pldmRes); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_SEND_NC_PLDM_REPLY, pldmRes->resp_size, pldmRes->common); if (ret) { goto free_exit; } nl_resp = send_nl_msg_retry(nl_msg); if (!nl_resp) { ret = -1; goto free_exit; } //print_ncsi_resp(nl_resp); free(nl_resp); nl_resp = NULL; if ((pldmCmd == CMD_APPLY_COMPLETE) || (pldmCmdStatus == -1)) break; if (nl_conf == 0) // Linux 4.1 msleep(10); // add dealy to reduce retry sending request to NIC } else { printf("unknown PLDM cmd 0x%x\n", pldmCmd); waitcycle++; if (waitcycle >= MAX_WAIT_CYCLE) { printf("max wait cycle exceeded, exit\n"); break; } } } // only activate FW if update loop exists with good status if (!pldmCmdStatus && (pldmCmd == CMD_APPLY_COMPLETE)) { // update successful, activate FW memset(&pldmReq, 0, sizeof(pldm_cmd_req)); pldmCreateActivateFirmwareCmd(&pldmReq); printf("\n05 PldmActivateFirmwareOp\n"); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { goto free_exit; } if (sendPldmCmdAndCheckResp(nl_msg) != CC_SUCCESS) { for (j=0; j<5; j++) { sleep(2); memset(&pldmReq, 0, sizeof(pldm_cmd_req)); pldmCreateGetStatusCmd(&pldmReq); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { printf("\nPldmGetStatus fail ret=%d\n",ret); goto free_exit; } nl_resp = send_nl_msg(nl_msg); if (!nl_resp) { printf("\nPldmGetStatus send_nl_msg fail\n"); ret = -1; goto free_exit; } print_pldm_cmd_status(nl_resp); print_pldm_resp_raw(nl_resp); currnet_state = nl_resp->msg_payload[8]; previous_state = nl_resp->msg_payload[9]; free(nl_resp); nl_resp = NULL; if (currnet_state == STATE_IDLE && previous_state == STATE_ACTIVATE) { ret = 0; break; } else { ret = -1; } } } } else { printf("PLDM cmd (%d) failed (status %d), abort update\n", pldmCmd, pldmCmdStatus); // send abort update cmd memset(&pldmReq, 0, sizeof(pldm_cmd_req)); pldmCreateCancelUpdateCmd(&pldmReq); ret = create_ncsi_ctrl_pkt(nl_msg, ch, NCSI_PLDM_REQUEST, pldmReq.payload_size, &(pldmReq.common[0])); if (ret) { ret = -1; goto free_exit; } // ignore the return status and exit since this is on the error path sendPldmCmdAndCheckResp(nl_msg); ret = -1; } free_exit: if (pkgHdr) free_pldm_pkg_data(&pkgHdr); if (nl_resp) free(nl_resp); if (pldmRes) free(pldmRes); if (nl_msg) free(nl_msg); return ret; } // Help for common command line arguments void ncsi_util_common_usage(void) { printf(" -n netdev Specifies the net device to send command to [default=\"eth0\"]\n"); printf(" -c channel Specifies the NC-SI channel on the net device [default=0]\n"); printf(" -l [config] netlink protocol [default=auto-detect]\n"); printf(" 0 - use NETLINK_USER (kernel 4.1)\n"); printf(" 1 - use libnl (kernel 5.0 and above)\n"); } static void default_ncsi_util_usage(void) { printf("Usage: ncsi-util [options] <cmd> \n\n"); printf(" -h This help\n"); ncsi_util_common_usage(); printf(" -m [vendor] OEM vendor specific command. Use -h for vendor specific usage\n"); printf(" brcm - Broadcom OEM command\n"); printf(" nvidia - Nvidia OEM command\n"); printf(" -S show adapter statistics\n"); printf(" -p [file] Update NIC firmware via PLDM\n"); printf(" -b [n] (optional) buffer size for PLDM FW update [default=1024]\n"); printf(" -z send \"PLDM Cancel Update\" cmd \n"); printf(" -s [n] socket test\n\n"); printf("Sample debug commands: \n"); printf(" ncsi-util -n eth0 -c 0 0x50 0 0 0x81 0x19 0 0 0x1b 0\n"); printf(" ncsi-util 0x8 (AEN Enable)\n"); printf(" ncsi-util 0xa (Get Link Status) \n"); printf(" ncsi-util 0x15 (Get Version ID) \n"); printf(" ncsi-util 0x16 (Get Capabilities) \n"); printf(" ncsi-util 0x17 (Get Parameters) \n"); printf(" ncsi-util 0x18/0x19/0x1a (Get Controller Packet/NC-SI/Pass-through statistics)\n\n"); } static int default_ncsi_util_handler(int argc, char **argv, ncsi_util_args_t *util_args) { int i = 0; NCSI_NL_MSG_T *msg = NULL; NCSI_NL_RSP_T *rsp = NULL; int argflag; int fshowethstats = 0; int cancelUpdate = 0; int bufSize = 0; char *pfile = NULL; int fupgrade = 0; int ret = 0; int sockettest = 0; /* * Handle ncsi DMTF command options */ while ((argflag = getopt(argc, argv, "hs:Sp:z?" NCSI_UTIL_COMMON_OPT_STR)) != -1) { switch(argflag) { case 'h': default_ncsi_util_usage(); return 0; case 's': bufSize = (int)strtoul(optarg, NULL, 0); sockettest = 1; break; case 'S': fshowethstats = 1; break; case 'p': bufSize = 1024; // buffer size for PLDM FW update [default=1024] pfile = optarg; printf ("Input file: \"%s\"\n", pfile); argflag = getopt(argc, (char **)argv, "b:"); if (argflag == 'b') { bufSize = (int)strtoul(optarg, NULL, 0); if (bufSize <= 0) { printf("bufSize %d is out of range.\n", bufSize); goto free_err_exit; } else { printf("bufSize = %d\n", bufSize); } } // if invalid opt str or missing argument, getopt returns a '?' else if (argflag == '?') { goto free_err_exit; } // if "b" is not found, getopt returns -1, in this case, continue, // and uses default size else if (argflag == -1) { printf("use default buf size %d\n", bufSize); } else { goto free_err_exit; } fupgrade = 1; break; case 'z': cancelUpdate = 1; printf ("Cancel firmware update...\n"); break; case NCSI_UTIL_GETOPT_COMMON_OPT_CHARS: // Already handled break; default : goto free_err_exit; } } msg = calloc(1, sizeof(NCSI_NL_MSG_T)); if (!msg) { printf("Error: failed buffer allocation\n"); return -1; } ncsi_util_init_pkt_by_args(msg, util_args); if (fshowethstats) { msg->cmd = NCSI_GET_CONTROLLER_PACKET_STATISTICS; msg->payload_length =0; } else if (cancelUpdate) { msg->cmd = NCSI_PLDM_REQUEST; // hard code "PLDM cancel update" for debug purpose msg->payload_length = 3; msg->msg_payload[0] = 0x84; // Cmd req, iid 1 msg->msg_payload[1] = PLDM_TYPE_FIRMWARE_UPDATE; msg->msg_payload[2] = CMD_CANCEL_UPDATE; } else if (!fupgrade) { int tmp_cmd = (int)strtoul(argv[optind++], NULL, 0); if (tmp_cmd <= 0xFF) { msg->cmd = tmp_cmd; } else { goto free_err_exit; } msg->payload_length = argc - optind; for (i=0; i<msg->payload_length; ++i) { msg->msg_payload[i] = (int)strtoul(argv[i + optind], NULL, 0); } } if(sockettest) { msg->cmd = 0xDE; msg->payload_length = bufSize; printf("socket test, size =%d\n",bufSize); for (i=0; i<msg->payload_length; ++i) { msg->msg_payload[i] = i; } } if (fupgrade) { // special case - invoke PLDM fw upgrade ret = pldm_update_fw(pfile, bufSize, msg->channel_id); } else { // send individual NCSI cmds #ifdef DEBUG printf("debug prints:"); printf("dev = %s\n", msg->dev_name); printf("cmd = 0x%x, channel = 0x%x, payload_length=0x%x\n", msg->cmd, msg->channel_id, msg->payload_length); for (i=0; i<msg->payload_length; i++) { if (i && !(i%16)) printf("\n"); printf("0x%x ", msg->msg_payload[i]); } printf("\n"); #endif rsp = send_nl_msg(msg); if (rsp) { if (msg->cmd == NCSI_PLDM_REQUEST) { if (get_cmd_status(rsp) != RESP_COMMAND_COMPLETED) { // command failed, likely due to device not supporting PLDM over NCSI print_ncsi_completion_codes(rsp); } else { // debug prints print_pldm_cmd_status(rsp); print_pldm_resp_raw(rsp); pldm_response *pldmRes = NULL; pldmRes = calloc(1, sizeof(pldm_response)); if (!pldmRes) { printf("%s, Error: failed pldmRes buffer allocation(%d)\n", __FUNCTION__, sizeof(pldm_response)); } pldmRes->resp_size = rsp->hdr.payload_length - 4; memcpy(&(pldmRes->common[0]), &(rsp->msg_payload[4]), pldmRes->resp_size); // handle command response pldmRespHandler(pldmRes); } } else { print_ncsi_resp(rsp); } free(rsp); } else { if(!sockettest) goto free_err_exit; } } free(msg); return ret; free_err_exit: if (msg) free(msg); default_ncsi_util_usage(); return -1; } static ncsi_util_vendor_t vendors[NCSI_UTIL_VENDOR_MAX] = { [NCSI_UTIL_VENDOR_DMTF] = { .name = "dmtf", .handler = default_ncsi_util_handler, }, [NCSI_UTIL_VENDOR_BRCM] = { .name = "brcm", .handler = brcm_ncsi_util_handler, }, [NCSI_UTIL_VENDOR_NVIDIA] = { .name = "nvidia", .handler = nvidia_ncsi_util_handler, }, }; // Find vendor by name static ncsi_util_vendor_t *find_vendor_by_name(const char *name) { int i; for (i = 0; i < NCSI_UTIL_VENDOR_MAX; i++) { if (!strcmp(vendors[i].name, name)) return &vendors[i]; } return NULL; } int main(int argc, char **argv) { ncsi_util_vendor_t *vendor = &vendors[NCSI_UTIL_VENDOR_DMTF]; // default int argflag; int i; ncsi_util_args_t util_args; if (argc < 2) goto err_exit; // Initialize util args ncsi_util_init_args(&util_args); /* * Handle util common options. * It ignores errors for other options and prefixes the option string * with '-' to prevent getopt changing the order of the arguments in argv. */ opterr = 0; while ((argflag = getopt(argc, argv, "-"NCSI_UTIL_COMMON_OPT_STR)) != -1) { switch(argflag) { case 'n': util_args.dev_name = optarg; break; case 'c': i = (int)strtoul(optarg, NULL, 0); if (i < 0) { printf("channel %d is out of range.\n", i); goto err_exit; } util_args.channel_id = i; break; case 'l': nl_conf = (int)strtoul(optarg, NULL, 0); if ((nl_conf < 0) || (nl_conf > 1)) { printf("invalid nl_conf\n"); goto err_exit; } break; case 'm': vendor = find_vendor_by_name(optarg); if (!vendor) { printf("unknown oem vendor\n"); goto err_exit; } break; default : break; } } // Determine netlink send function determine_nl_method(); // Configure NC-SI lib to use printf for logging for this util ncsi_config_log(NCSI_LOG_METHOD_PRINTF); optind = 0; opterr = 1; return vendor->handler(argc, argv, &util_args); err_exit: default_ncsi_util_usage(); return -1; }