/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2013 Red Hat, Inc. */ #include #include #include #include #include "nm-dcb.h" #include "nm-platform.h" #include "NetworkManagerUtils.h" #include "nm-posix-signals.h" #include "nm-logging.h" GQuark nm_dcb_error_quark (void) { static GQuark ret = 0; if (ret == 0) ret = g_quark_from_static_string ("nm-dcb-error"); return ret; } static const char *helper_names[] = { "dcbtool", "fcoeadm" }; gboolean do_helper (const char *iface, guint which, DcbFunc run_func, gpointer user_data, GError **error, const char *fmt, ...) { char **argv = NULL, **split = NULL, *cmdline, *errmsg = NULL; gboolean success = FALSE; guint i, u; va_list args; g_return_val_if_fail (fmt != NULL, FALSE); va_start (args, fmt); cmdline = g_strdup_vprintf (fmt, args); va_end (args); split = g_strsplit_set (cmdline, " ", 0); if (!split) { g_set_error (error, NM_DCB_ERROR, NM_DCB_ERROR_INTERNAL, "failure parsing %s command line", helper_names[which]); goto out; } /* Allocate space for path, custom arg, interface name, arguments, and NULL */ i = u = 0; argv = g_new0 (char *, g_strv_length (split) + 4); argv[i++] = NULL; /* Placeholder for dcbtool path */ if (which == DCBTOOL) { argv[i++] = "sc"; argv[i++] = (char *) iface; } while (u < g_strv_length (split)) argv[i++] = split[u++]; argv[i++] = NULL; success = run_func (argv, which, user_data, error); if (!success && error) g_assert (*error); out: if (split) g_strfreev (split); g_free (argv); g_free (cmdline); g_free (errmsg); return success; } #define SET_FLAGS(f, tag) \ G_STMT_START { \ if (!do_helper (iface, DCBTOOL, run_func, user_data, error, tag " e:%c a:%c w:%c", \ f & NM_SETTING_DCB_FLAG_ENABLE ? '1' : '0', \ f & NM_SETTING_DCB_FLAG_ADVERTISE ? '1' : '0', \ f & NM_SETTING_DCB_FLAG_WILLING ? '1' : '0')) \ return FALSE; \ } G_STMT_END #define SET_APP(f, s, tag) \ G_STMT_START { \ gint prio = nm_setting_dcb_get_app_##tag##_priority (s); \ \ SET_FLAGS (f, "app:" #tag); \ if ((f & NM_SETTING_DCB_FLAG_ENABLE) && (prio >= 0)) { \ if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "app:" #tag " appcfg:%02x", (1 << prio))) \ return FALSE; \ } \ } G_STMT_END gboolean _dcb_setup (const char *iface, NMSettingDcb *s_dcb, DcbFunc run_func, gpointer user_data, GError **error) { NMSettingDcbFlags flags; guint i; g_assert (s_dcb); if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb on")) return FALSE; /* FCoE */ flags = nm_setting_dcb_get_app_fcoe_flags (s_dcb); SET_APP (flags, s_dcb, fcoe); /* iSCSI */ flags = nm_setting_dcb_get_app_iscsi_flags (s_dcb); SET_APP (flags, s_dcb, iscsi); /* FIP */ flags = nm_setting_dcb_get_app_fip_flags (s_dcb); SET_APP (flags, s_dcb, fip); /* Priority Flow Control */ flags = nm_setting_dcb_get_priority_flow_control_flags (s_dcb); SET_FLAGS (flags, "pfc"); if (flags & NM_SETTING_DCB_FLAG_ENABLE) { char buf[10]; for (i = 0; i < 8; i++) buf[i] = nm_setting_dcb_get_priority_flow_control (s_dcb, i) ? '1' : '0'; buf[i] = 0; if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pfc pfcup:%s", buf)) return FALSE; } /* Priority Groups */ flags = nm_setting_dcb_get_priority_group_flags (s_dcb); SET_FLAGS (flags, "pg"); if (flags & NM_SETTING_DCB_FLAG_ENABLE) { char buf[10]; guint id; /* Priority Groups */ for (i = 0; i < 8; i++) { id = nm_setting_dcb_get_priority_group_id (s_dcb, i); g_assert (id < 8 || id == 15); buf[i] = (id < 8) ? ('0' + id) : 'f'; } buf[i] = 0; if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pg pgid:%s", buf)) return FALSE; /* Priority Group Bandwidth */ if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pg pgpct:%u,%u,%u,%u,%u,%u,%u,%u", nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 0), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 1), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 2), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 3), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 4), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 5), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 6), nm_setting_dcb_get_priority_group_bandwidth (s_dcb, 7))) return FALSE; /* Priority Bandwidth */ if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pg uppct:%u,%u,%u,%u,%u,%u,%u,%u", nm_setting_dcb_get_priority_bandwidth (s_dcb, 0), nm_setting_dcb_get_priority_bandwidth (s_dcb, 1), nm_setting_dcb_get_priority_bandwidth (s_dcb, 2), nm_setting_dcb_get_priority_bandwidth (s_dcb, 3), nm_setting_dcb_get_priority_bandwidth (s_dcb, 4), nm_setting_dcb_get_priority_bandwidth (s_dcb, 5), nm_setting_dcb_get_priority_bandwidth (s_dcb, 6), nm_setting_dcb_get_priority_bandwidth (s_dcb, 7))) return FALSE; /* Strict Bandwidth */ for (i = 0; i < 8; i++) buf[i] = nm_setting_dcb_get_priority_strict_bandwidth (s_dcb, i) ? '1' : '0'; buf[i] = 0; if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pg strict:%s", buf)) return FALSE; /* Priority Traffic Class */ for (i = 0; i < 8; i++) { id = nm_setting_dcb_get_priority_traffic_class (s_dcb, i); g_assert (id < 8); buf[i] = '0' + id; } buf[i] = 0; if (!do_helper (iface, DCBTOOL, run_func, user_data, error, "pg up2tc:%s", buf)) return FALSE; } return TRUE; } gboolean _dcb_cleanup (const char *iface, DcbFunc run_func, gpointer user_data, GError **error) { /* FIXME: do we need to turn off features individually here? */ return do_helper (iface, DCBTOOL, run_func, user_data, error, "dcb off"); } gboolean _fcoe_setup (const char *iface, NMSettingDcb *s_dcb, DcbFunc run_func, gpointer user_data, GError **error) { NMSettingDcbFlags flags; g_assert (s_dcb); flags = nm_setting_dcb_get_app_fcoe_flags (s_dcb); if (flags & NM_SETTING_DCB_FLAG_ENABLE) { const char *mode = nm_setting_dcb_get_app_fcoe_mode (s_dcb); if (!do_helper (NULL, FCOEADM, run_func, user_data, error, "-m %s -c %s", mode, iface)) return FALSE; } else { if (!do_helper (NULL, FCOEADM, run_func, user_data, error, "-d %s", iface)) return FALSE; } return TRUE; } gboolean _fcoe_cleanup (const char *iface, DcbFunc run_func, gpointer user_data, GError **error) { return do_helper (NULL, FCOEADM, run_func, user_data, error, "-d %s", iface); } static const char *dcbpaths[] = { "/sbin/dcbtool", "/usr/sbin/dcbtool", "/usr/local/sbin/dcbtool", NULL }; static const char *fcoepaths[] = { "/sbin/fcoeadm", "/usr/sbin/fcoeadm", "/usr/local/sbin/fcoeadm", NULL }; static gboolean run_helper (char **argv, guint which, gpointer user_data, GError **error) { static const char *helper_path[2] = { NULL, NULL }; int exit_status = 0; gboolean success; char *errmsg = NULL, *outmsg = NULL; const char **iter; char *cmdline; if (G_UNLIKELY (helper_path[which] == NULL)) { iter = (which == DCBTOOL) ? dcbpaths : fcoepaths; while (*iter) { if (g_file_test (*iter, G_FILE_TEST_EXISTS)) helper_path[which] = *iter; iter++; } if (!helper_path[which]) { g_set_error (error, NM_DCB_ERROR, NM_DCB_ERROR_HELPER_NOT_FOUND, "%s not found", which == DCBTOOL ? "dcbtool" : "fcoadm"); return FALSE; } } argv[0] = (char *) helper_path[which]; cmdline = g_strjoinv (" ", argv); nm_log_dbg (LOGD_DCB, "%s", cmdline); success = g_spawn_sync ("/", argv, NULL, 0 /*G_SPAWN_DEFAULT*/, nm_unblock_posix_signals, NULL, &outmsg, &errmsg, &exit_status, error); /* Log any stderr output */ if (success && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) && (errmsg || outmsg)) { nm_log_dbg (LOGD_DCB, "'%s' failed: '%s'", cmdline, (errmsg && strlen (errmsg)) ? errmsg : outmsg); g_set_error (error, NM_DCB_ERROR, NM_DCB_ERROR_HELPER_FAILED, "Failed to run '%s'", cmdline); success = FALSE; } g_free (errmsg); g_free (cmdline); return success; } gboolean nm_dcb_setup (const char *iface, NMSettingDcb *s_dcb, GError **error) { gboolean success; success = _dcb_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (DCBTOOL), error); if (success) success = _fcoe_setup (iface, s_dcb, run_helper, GUINT_TO_POINTER (FCOEADM), error); return success; } gboolean nm_dcb_cleanup (const char *iface, GError **error) { return _dcb_cleanup (iface, run_helper, GUINT_TO_POINTER (DCBTOOL), error); }