static ni_bool_t ni_ifdown_stop_device(const char *device_name) { if (!ni_nanny_call_device_disable(device_name)) { ni_debug_application("Unable to disable device named %s", device_name); return FALSE; } return TRUE; }
/* * Wickedd is sending us a signal indicating internal device state change. * We want to wait for this signal and when it is >= device-up return TRUE. * After timeout we fail... */ void ni_state_change_signal_handler(ni_dbus_connection_t *conn, ni_dbus_message_t *msg, void *user_data) { const char *signal_name = dbus_message_get_member(msg); const char *object_path = dbus_message_get_path(msg); ni_dbus_variant_t argv = NI_DBUS_VARIANT_INIT; ni_ifworker_array_t *ifworkers = user_data; ni_fsm_state_t cur_state, target_state; const char *ifname; unsigned int i; if (ni_string_empty(object_path)) return; /* Deserialize dbus message */ if (ni_dbus_message_get_args_variants(msg, &argv, 1) < 0 || !ni_dbus_variant_is_dict(&argv)) { ni_error("Unable to retrieve dict from signal %s, object_path=%s", signal_name, object_path); return; } if (!ni_dbus_dict_get_uint32(&argv, "current-state", &cur_state) || !ni_dbus_dict_get_uint32(&argv, "target-state", &target_state) || !ni_dbus_dict_get_string(&argv, "ifname", &ifname)) { ni_error("Unable to retrieve dict's values from signal %s, object_path=%s", signal_name, object_path); return; } ni_debug_application("received signal %s; object_path=%s; target_state=%s, state_name=%s", signal_name, object_path, ni_ifworker_state_name(target_state), ni_ifworker_state_name(cur_state)); for (i = 0; i < ifworkers->count; ++i) { ni_ifworker_t *w = ifworkers->data[i]; if (cur_state != NI_FSM_STATE_NONE && cur_state != target_state) continue; if (!ni_string_eq(w->name, ifname)) continue; ni_warn("%s: Device %s", ifname, cur_state == target_state ? "succeeded" : "failed"); ni_ifworker_array_remove_with_children(ifworkers, w); } ni_dbus_variant_destroy(&argv); }
static ni_bool_t ni_ifdown_stop_policy(const char *policy_name) { /* Default policy name is interface name. * In case of any change other parameters * should be available within ifworker */ if (!ni_nanny_call_del_policy(policy_name)) { ni_debug_application("Unable to delete policy named %s", policy_name); return FALSE; } return TRUE; }
static ni_bool_t __do_arp_validate_send(struct arp_handle *handle) { struct in_addr null = { 0 }; ni_bool_t ret = FALSE; if (!handle->hwaddr.len && handle->nprobes) { ni_debug_application("%s: arp validate: probing for %s", handle->ifname, ni_sockaddr_print(&handle->ipaddr)); handle->nprobes--; handle->replies = TRUE; if ((ret = ni_arp_send_request(handle->sock, null, handle->ipaddr.sin.sin_addr) > 0)) { __do_arp_validate_arm_timer(handle); } } else if (!handle->hwaddr.len && handle->nclaims) { ni_debug_application("%s: arp validate: claiming %s use", handle->ifname, ni_sockaddr_print(&handle->ipaddr)); handle->nclaims--; handle->replies = FALSE; if ((ret = ni_arp_send_grat_request(handle->sock, handle->ipaddr.sin.sin_addr) > 0)) { if (handle->nclaims) { __do_arp_validate_arm_timer(handle); } else if (handle->sock) { __do_arp_handle_close(handle); } } } return ret; }
static void show_exec_info(int argc, char **argv) { ni_stringbuf_t args = NI_STRINGBUF_INIT_DYNAMIC; int i; for (i = 0; i < argc && argv[i]; ++i) { if (i != 0) ni_stringbuf_putc(&args, ' '); ni_stringbuf_puts(&args, argv[i]); } ni_debug_application("Executing: %s", args.string); ni_stringbuf_destroy(&args); }
static void __do_arp_validate_process(ni_arp_socket_t *sock, const ni_arp_packet_t *pkt, void *user_data) { struct arp_handle *handle = user_data; ni_netconfig_t *nc = ni_global_state_handle(0); const ni_netdev_t *ifp; ni_bool_t false_alarm = FALSE; ni_bool_t found_addr = FALSE; const ni_address_t *ap; ni_sockaddr_t addr; if (!pkt || pkt->op != ARPOP_REPLY || !handle->replies) return; /* Is it about the address we're validating at all? */ if (pkt->sip.s_addr != handle->ipaddr.sin.sin_addr.s_addr) { ni_debug_application("%s: report about different address", handle->ifname); return; } /* Ignore any ARP replies that seem to come from our own * MAC address. Some helpful switches seem to generate * these. */ if (ni_link_address_equal(&sock->dev_info.hwaddr, &pkt->sha)) { ni_debug_application("%s: adress in use by ourself", handle->ifname); return; } /* As well as ARP replies that seem to come from our own * host: dup if same address, not a dup if there are two * interfaces connected to the same broadcast domain. */ ni_sockaddr_set_ipv4(&addr, pkt->sip, 0); for (ifp = ni_netconfig_devlist(nc); ifp; ifp = ifp->next) { if (ifp->link.ifindex == sock->dev_info.ifindex) continue; if (!ni_netdev_link_is_up(ifp)) continue; if (!ni_link_address_equal(&ifp->link.hwaddr, &pkt->sha)) continue; /* OK, we have an interface matching the hwaddr, * which will answer arp requests when it is on * the same broadcast domain and causes a false * alarm, except it really has the IP assigned. */ false_alarm = TRUE; for (ap = ifp->addrs; !found_addr && ap; ap = ap->next) { if (ap->family != AF_INET) continue; if (ni_sockaddr_equal(&ap->local_addr, &addr)) found_addr = TRUE; } } if (false_alarm && !found_addr) { ni_debug_application("%s: reply from one of our interfaces", handle->ifname); return; } ni_info("%s: adress %s in use by %s reported", handle->ifname, inet_ntoa(pkt->sip), ni_link_address_print(&pkt->sha)); handle->hwaddr = pkt->sha; }
static int ni_do_ifup_direct(int argc, char **argv) { enum { OPT_HELP, OPT_IFCONFIG, OPT_CONTROL_MODE, OPT_STAGE, OPT_TIMEOUT, OPT_SKIP_ACTIVE, OPT_SKIP_ORIGIN, OPT_PERSISTENT, OPT_TRANSIENT, #ifdef NI_TEST_HACKS OPT_IGNORE_PRIO, OPT_IGNORE_STARTMODE, #endif }; static struct option ifup_options[] = { { "help", no_argument, NULL, OPT_HELP }, { "ifconfig", required_argument, NULL, OPT_IFCONFIG }, { "mode", required_argument, NULL, OPT_CONTROL_MODE }, { "boot-stage", required_argument, NULL, OPT_STAGE }, { "skip-active",required_argument, NULL, OPT_SKIP_ACTIVE }, { "skip-origin",required_argument, NULL, OPT_SKIP_ORIGIN }, { "timeout", required_argument, NULL, OPT_TIMEOUT }, { "transient", no_argument, NULL, OPT_TRANSIENT }, #ifdef NI_TEST_HACKS { "ignore-prio",no_argument, NULL, OPT_IGNORE_PRIO }, { "ignore-startmode",no_argument, NULL, OPT_IGNORE_STARTMODE }, #endif { "persistent", no_argument, NULL, OPT_PERSISTENT }, { NULL } }; ni_ifmatcher_t ifmatch; ni_ifmarker_t ifmarker; ni_ifworker_array_t ifmarked; ni_string_array_t opt_ifconfig = NI_STRING_ARRAY_INIT; ni_bool_t check_prio = TRUE; ni_bool_t opt_transient = FALSE; unsigned int nmarked; ni_fsm_t *fsm; int c, status = NI_WICKED_RC_USAGE; unsigned int timeout = 0; const char *ptr; fsm = ni_fsm_new(); ni_assert(fsm); ni_fsm_require_register_type("reachable", ni_ifworker_reachability_check_new); memset(&ifmatch, 0, sizeof(ifmatch)); memset(&ifmarker, 0, sizeof(ifmarker)); memset(&ifmarked, 0, sizeof(ifmarked)); /* Allow ifup on all interfaces we have config for */ ifmatch.require_configured = FALSE; ifmatch.allow_persistent = TRUE; ifmatch.require_config = TRUE; ifmarker.target_range.min = NI_FSM_STATE_ADDRCONF_UP; ifmarker.target_range.max = __NI_FSM_STATE_MAX; /* * Workaround to consider WAIT_FOR_INTERFACES variable * in network/config (bnc#863371, bnc#862530 timeouts). * Correct would be to get it from compat layer, but * the network/config is sourced in systemd service... */ if ((ptr = getenv("WAIT_FOR_INTERFACES"))) { unsigned int sec; if (ni_parse_uint(ptr, &sec, 10) == 0 && (sec * 1000 > fsm->worker_timeout)) { ni_debug_application("wait %u sec for interfaces", sec); timeout = sec * 1000; } } optind = 1; while ((c = getopt_long(argc, argv, "", ifup_options, NULL)) != EOF) { switch (c) { case OPT_IFCONFIG: ni_string_array_append(&opt_ifconfig, optarg); break; case OPT_CONTROL_MODE: ifmatch.mode = optarg; break; case OPT_STAGE: ifmatch.boot_stage= optarg; break; case OPT_TIMEOUT: if (!strcmp(optarg, "infinite")) { timeout = NI_IFWORKER_INFINITE_TIMEOUT; } else if (ni_parse_uint(optarg, &timeout, 10) >= 0) { timeout *= 1000; /* sec -> msec */ } else { ni_error("ifup: cannot parse timeout option \"%s\"", optarg); goto usage; } break; case OPT_SKIP_ORIGIN: ifmatch.skip_origin = optarg; break; case OPT_SKIP_ACTIVE: ifmatch.skip_active = TRUE; break; #ifdef NI_TEST_HACKS case OPT_IGNORE_PRIO: check_prio = FALSE; break; case OPT_IGNORE_STARTMODE: ifmatch.ignore_startmode = TRUE; break; #endif case OPT_PERSISTENT: ifmarker.persistent = TRUE; break; case OPT_TRANSIENT: opt_transient = TRUE; break; default: case OPT_HELP: usage: fprintf(stderr, "wicked [options] ifup [ifup-options] <ifname ...>|all\n" "\nSupported ifup-options:\n" " --help\n" " Show this help text.\n" " --transient\n" " Enable transient interface return codes\n" " --ifconfig <pathname>\n" " Read interface configuration(s) from file/directory rather than using system config\n" " --mode <label>\n" " Only touch interfaces with matching control <mode>\n" " --boot-stage <label>\n" " Only touch interfaces with matching <boot-stage>\n" " --skip-active\n" " Do not touch running interfaces\n" " --skip-origin <name>\n" " Skip interfaces that have a configuration origin of <name>\n" " Usually, you would use this with the name \"firmware\" to avoid\n" " touching interfaces that have been set up via firmware (like iBFT) previously\n" " --timeout <nsec>\n" " Timeout after <nsec> seconds\n" #ifdef NI_TEST_HACKS " --ignore-prio\n" " Ignore checking the config origin priorities\n" " --ignore-startmode\n" " Ignore checking the STARTMODE=off and STARTMODE=manual configs\n" #endif " --persistent\n" " Set interface into persistent mode (no regular ifdown allowed)\n" ); goto cleanup; } } if (optind >= argc) { fprintf(stderr, "Missing interface argument\n"); goto usage; } if (!ni_fsm_create_client(fsm)) { /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } if (!ni_fsm_refresh_state(fsm)) { /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } if (opt_ifconfig.count == 0) { const ni_string_array_t *sources = ni_config_sources("ifconfig"); if (sources && sources->count) ni_string_array_copy(&opt_ifconfig, sources); if (opt_ifconfig.count == 0) { ni_error("ifup: unable to load interface config source list"); status = NI_WICKED_RC_NOT_CONFIGURED; goto cleanup; } } if (!ni_ifconfig_load(fsm, opt_global_rootdir, &opt_ifconfig, check_prio, TRUE)) { status = NI_WICKED_RC_NOT_CONFIGURED; goto cleanup; } /* Client waits for device-up events for WAIT_FOR_INTERFACES */ if (timeout) ni_wait_for_interfaces = timeout; /* One set by user */ else ni_wait_for_interfaces *= 1000; /* in msec */ if (ni_fsm_build_hierarchy(fsm) < 0) { ni_error("ifup: unable to build device hierarchy"); /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } /* Get workers that match given criteria */ nmarked = 0; while (optind < argc) { ifmatch.name = argv[optind++]; if (!strcmp(ifmatch.name, "boot")) { ifmatch.name = "all"; ifmatch.mode = "boot"; } ni_fsm_get_matching_workers(fsm, &ifmatch, &ifmarked); } ni_ifup_pull_in_children(&ifmarked); /* Mark and start selected workers */ if (ifmarked.count) nmarked = ni_fsm_mark_matching_workers(fsm, &ifmarked, &ifmarker); if (nmarked == 0) { printf("ifup: no matching interfaces\n"); status = NI_WICKED_RC_SUCCESS; } else { if (ni_fsm_schedule(fsm) != 0) ni_fsm_mainloop(fsm); /* No error if all interfaces were good */ status = ni_fsm_fail_count(fsm) ? NI_WICKED_RC_ERROR : NI_WICKED_RC_SUCCESS; /* Do not report any transient errors to systemd (e.g. dhcp * or whatever not ready in time) -- returning an error may * cause to stop the network completely. */ if (!opt_transient) status = NI_LSB_RC_SUCCESS; } cleanup: ni_ifworker_array_destroy(&ifmarked); ni_string_array_destroy(&opt_ifconfig); return status; }
static ni_bool_t ni_ifup_hire_nanny(ni_ifworker_t *w) { xml_node_t *ifcfg = NULL, *policy = NULL; ni_netdev_t *dev; unsigned int i; ni_bool_t rv = FALSE; char *pname; if (!w) return rv; ni_debug_application("%s: hiring nanny", w->name); /* Create a config duplicate for a policy */ ifcfg = xml_node_clone(w->config.node, NULL); if (!ifcfg) goto error; pname = ni_ifpolicy_name_from_ifname(w->name); ni_debug_application("%s: converting config into policy '%s'", w->name, pname); policy = ni_convert_cfg_into_policy_node(ifcfg, __ni_ifup_generate_match(w), pname, w->config.origin); ni_string_free(&pname); if (!policy) { policy = ifcfg; /* Free cloned config*/ goto error; } #if 0 /* Do we need this? */ /* Add link type to match node*/ if (dev) { ni_debug_application("%s: adding link type (%s) to match", w->name, ni_linktype_type_to_name(dev->link.type)); ni_ifpolicy_match_add_link_type(policy, dev->link.type); } ni_debug_application("%s: adding minimum device state (%s) to match", w->name, ni_ifworker_state_name(w->fsm.state)); /* Add minimum device state to match node */ if (!ni_ifpolicy_match_add_min_state(policy, w->fsm.state)) goto error; #endif dev = w->device; if (dev) { ni_debug_application("%s: enabling device for nanny", w->name); if (!ni_nanny_call_device_enable(w->name)) goto error; } ni_debug_application("%s: adding policy %s to nanny", w->name, xml_node_get_attr(policy, NI_NANNY_IFPOLICY_NAME)); if (ni_nanny_addpolicy_node(policy, w->config.origin) <= 0) { ni_nanny_call_device_disable(w->name); goto error; } ni_debug_application("%s: nanny hired!", w->name); ni_ifworker_success(w); /* Append policies for all children in case they contain some special options */ for (i = 0; i < w->children.count; i++) { ni_ifworker_t *child = w->children.data[i]; if (!ni_ifup_hire_nanny(child)) ni_error("%s: unable to apply configuration to nanny", child->name); } rv = TRUE; error: if (!rv) ni_ifworker_fail(w, "%s: unable to apply configuration to nanny", w->name); xml_node_free(policy); return rv; }
int ni_do_ifreload(int argc, char **argv) { enum { OPT_HELP, OPT_IFCONFIG, OPT_PERSISTENT, OPT_TRANSIENT, #ifdef NI_TEST_HACKS OPT_IGNORE_PRIO, OPT_IGNORE_STARTMODE, #endif }; static struct option ifreload_options[] = { { "help", no_argument, NULL, OPT_HELP }, { "transient", no_argument, NULL, OPT_TRANSIENT }, { "ifconfig", required_argument, NULL, OPT_IFCONFIG }, #ifdef NI_TEST_HACKS { "ignore-prio",no_argument, NULL, OPT_IGNORE_PRIO }, { "ignore-startmode",no_argument, NULL, OPT_IGNORE_STARTMODE }, #endif { "persistent", no_argument, NULL, OPT_PERSISTENT }, { NULL, no_argument, NULL, 0 } }; ni_string_array_t opt_ifconfig = NI_STRING_ARRAY_INIT; ni_ifworker_array_t marked = { 0, NULL }; ni_ifmatcher_t ifmatch; ni_bool_t check_prio = TRUE; ni_bool_t opt_persistent = FALSE; ni_bool_t opt_transient = FALSE; int c, status = NI_WICKED_RC_USAGE; unsigned int nmarked, i; const ni_uint_range_t up_range = { .min = NI_FSM_STATE_ADDRCONF_UP, .max = __NI_FSM_STATE_MAX }; const char *ptr; ni_fsm_t *fsm; /* Allow ifreload on all interfaces with a changed config */ memset(&ifmatch, 0, sizeof(ifmatch)); ifmatch.require_configured = FALSE; ifmatch.allow_persistent = TRUE; ifmatch.require_config = FALSE; fsm = ni_fsm_new(); ni_assert(fsm); ni_fsm_require_register_type("reachable", ni_ifworker_reachability_check_new); /* * Workaround to consider WAIT_FOR_INTERFACES variable * in network/config (bnc#863371, bnc#862530 timeouts). * Correct would be to get it from compat layer, but * the network/config is sourced in systemd service... */ if ((ptr = getenv("WAIT_FOR_INTERFACES"))) { unsigned int sec; if (ni_parse_uint(ptr, &sec, 10) == 0 && (sec * 1000 > fsm->worker_timeout)) { ni_debug_application("wait %u sec for interfaces", sec); fsm->worker_timeout = sec * 1000; } } optind = 1; while ((c = getopt_long(argc, argv, "", ifreload_options, NULL)) != EOF) { switch (c) { case OPT_IFCONFIG: ni_string_array_append(&opt_ifconfig, optarg); break; #ifdef NI_TEST_HACKS case OPT_IGNORE_PRIO: check_prio = FALSE; break; case OPT_IGNORE_STARTMODE: ifmatch.ignore_startmode = TRUE; break; #endif case OPT_PERSISTENT: opt_persistent = TRUE; break; case OPT_TRANSIENT: opt_transient = TRUE; break; default: case OPT_HELP: usage: fprintf(stderr, "wicked [options] ifreload [ifreload-options] <ifname ...>|all\n" "\nSupported ifreload-options:\n" " --help\n" " Show this help text.\n" " --transient\n" " Enable transient interface return codes\n" " --ifconfig <filename>\n" " Read interface configuration(s) from file\n" #ifdef NI_TEST_HACKS " --ignore-prio\n" " Ignore checking the config origin priorities\n" " --ignore-startmode\n" " Ignore checking the STARTMODE=off and STARTMODE=manual configs\n" #endif " --persistent\n" " Set interface into persistent mode (no regular ifdown allowed)\n" ); goto cleanup; } } /* at least one argument is required */ if (optind >= argc) { fprintf(stderr, "Missing interface argument\n"); goto usage; } else for (c = optind; c < argc; ++c) { if (ni_string_empty(argv[c])) { printf("ARG: %s\n", argv[c]); goto usage; } } if (!ni_fsm_create_client(fsm)) { /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } if (!ni_fsm_refresh_state(fsm)) { /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } if (opt_ifconfig.count == 0) { const ni_string_array_t *sources = ni_config_sources("ifconfig"); if (sources && sources->count) ni_string_array_copy(&opt_ifconfig, sources); if (opt_ifconfig.count == 0) { ni_error("ifreload: unable to load interface config source list"); status = NI_WICKED_RC_NOT_CONFIGURED; goto cleanup; } } if (!ni_ifconfig_load(fsm, opt_global_rootdir, &opt_ifconfig, check_prio, TRUE)) { status = NI_WICKED_RC_NOT_CONFIGURED; goto cleanup; } status = NI_WICKED_RC_SUCCESS; nmarked = 0; for (c = optind; c < argc; ++c) { ni_ifworker_array_t temp = { 0, NULL }; /* Getting an array of ifworkers matching argument */ ifmatch.name = argv[c]; if (!ni_fsm_get_matching_workers(fsm, &ifmatch, &temp)) continue; for (i = 0; i < temp.count; ++i) { ni_ifworker_t *w = temp.data[i]; ni_netdev_t *dev = w->device; /* skip duplicate matches */ if (ni_ifworker_array_index(&marked, w) != -1) continue; /* skip unused devices without config */ if (!ni_ifcheck_worker_config_exists(w) && !ni_ifcheck_device_configured(dev)) continue; /* skip if config changed somehow */ if (ni_ifcheck_worker_config_matches(w)) continue; /* Mark persistend when requested */ if (opt_persistent) w->client_state.persistent = TRUE; /* Remember all changed devices */ ni_ifworker_array_append(&marked, w); /* Persistent do not go down but up only */ if (!dev || ni_ifcheck_device_is_persistent(dev)) continue; /* Decide how much down we go */ if (ni_ifcheck_worker_config_exists(w)) { if (!ni_ifcheck_device_configured(dev)) continue; w->target_range.min = NI_FSM_STATE_NONE; w->target_range.max = NI_FSM_STATE_DEVICE_READY; nmarked++; } else if (ni_ifcheck_device_configured(dev)) { w->target_range.min = NI_FSM_STATE_NONE; w->target_range.max = NI_FSM_STATE_DEVICE_DOWN; nmarked++; } } ni_ifworker_array_destroy(&temp); } if (nmarked) { /* Run ifdown part of the reload */ ni_debug_application("Shutting down unneeded devices"); ni_fsm_start_matching_workers(fsm, &marked); /* Execute the down run */ if (ni_fsm_schedule(fsm) != 0) ni_fsm_mainloop(fsm); } if (marked.count) { /* Drop deleted or apply the up range */ ni_fsm_reset_matching_workers(fsm, &marked, &up_range, FALSE); /* And trigger up */ ni_debug_application("Reloading all changed devices"); ni_ifup_pull_in_children(&marked); nmarked = ni_fsm_start_matching_workers(fsm, &marked); ni_ifworker_array_destroy(&marked); } if (nmarked) { /* Build the up tree */ if (ni_fsm_build_hierarchy(fsm) < 0) { ni_error("ifreload: unable to build device hierarchy"); /* Severe error we always explicitly return */ status = NI_WICKED_RC_ERROR; goto cleanup; } /* Execute the up run */ if (ni_fsm_schedule(fsm) != 0) ni_fsm_mainloop(fsm); /* No error if all interfaces were good */ status = ni_fsm_fail_count(fsm) ? NI_WICKED_RC_ERROR : NI_WICKED_RC_SUCCESS; /* Do not report any transient errors to systemd (e.g. dhcp * or whatever not ready in time) -- returning an error may * cause to stop the network completely. */ if (!opt_transient) status = NI_LSB_RC_SUCCESS; } cleanup: ni_string_array_destroy(&opt_ifconfig); ni_ifworker_array_destroy(&marked); return status; }
int ni_do_ifdown(int argc, char **argv) { enum { OPT_HELP, OPT_FORCE, OPT_DELETE, OPT_NO_DELETE, OPT_TIMEOUT }; static struct option ifdown_options[] = { { "help", no_argument, NULL, OPT_HELP }, { "force", required_argument, NULL, OPT_FORCE }, { "delete", no_argument, NULL, OPT_DELETE }, { "no-delete", no_argument, NULL, OPT_NO_DELETE }, { "timeout", required_argument, NULL, OPT_TIMEOUT }, { NULL } }; ni_ifmatcher_t ifmatch; ni_ifmarker_t ifmarker; ni_ifworker_array_t ifmarked; ni_string_array_t ifnames = NI_STRING_ARRAY_INIT; unsigned int nmarked, max_state = NI_FSM_STATE_DEVICE_DOWN; unsigned int timeout = NI_IFWORKER_DEFAULT_TIMEOUT; ni_stringbuf_t sb = NI_STRINGBUF_INIT_DYNAMIC; ni_fsm_t *fsm; int c, status = NI_WICKED_RC_USAGE; fsm = ni_fsm_new(); ni_assert(fsm); ni_fsm_require_register_type("reachable", ni_ifworker_reachability_check_new); /* Allow ifdown only on non-persistent interfaces previously configured by ifup */ memset(&ifmatch, 0, sizeof(ifmatch)); memset(&ifmarker, 0, sizeof(ifmarker)); memset(&ifmarked, 0, sizeof(ifmarked)); ifmatch.require_configured = TRUE; ifmatch.allow_persistent = FALSE; ifmatch.require_config = FALSE; ifmarker.target_range.min = NI_FSM_STATE_DEVICE_DOWN; ifmarker.target_range.max = __NI_FSM_STATE_MAX - 2; optind = 1; while ((c = getopt_long(argc, argv, "", ifdown_options, NULL)) != EOF) { switch (c) { case OPT_FORCE: if (!ni_ifworker_state_from_name(optarg, &max_state) || !ni_ifworker_state_in_range(&ifmarker.target_range, max_state)) { ni_error("ifdown: wrong force option \"%s\"", optarg); goto usage; } /* Allow ifdown on persistent, unconfigured interfaces */ ifmatch.require_configured = FALSE; ifmatch.allow_persistent = TRUE; ifmatch.require_config = FALSE; break; case OPT_DELETE: max_state = NI_FSM_STATE_DEVICE_DOWN; /* Allow ifdown on persistent, unconfigured interfaces */ ifmatch.require_configured = FALSE; ifmatch.allow_persistent = TRUE; ifmatch.require_config = FALSE; break; case OPT_NO_DELETE: max_state = NI_FSM_STATE_DEVICE_READY; /* Allow ifdown only on non-persistent interfaces previously configured by ifup */ ifmatch.require_configured = TRUE; ifmatch.allow_persistent = FALSE; ifmatch.require_config = FALSE; break; case OPT_TIMEOUT: if (!strcmp(optarg, "infinite")) { timeout = NI_IFWORKER_INFINITE_TIMEOUT; } else if (ni_parse_uint(optarg, &timeout, 10) >= 0) { timeout *= 1000; /* sec -> msec */ } else { ni_error("ifdown: cannot parse timeout option \"%s\"", optarg); goto usage; } break; default: case OPT_HELP: usage: ni_client_get_state_strings(&sb, &ifmarker.target_range); fprintf(stderr, "wicked [options] ifdown [ifdown-options] <ifname ...>|all\n" "\nSupported ifdown-options:\n" " --help\n" " Show this help text.\n" " --force <state>\n" " Force putting interface into the <state> state. Despite of persistent mode being set. Possible states:\n" " %s\n" " --delete\n" " Delete device. Despite of persistent mode being set\n" " --no-delete\n" " Do not attempt to delete a device, neither physical nor virtual\n" " --timeout <nsec>\n" " Timeout after <nsec> seconds\n", sb.string ); ni_stringbuf_destroy(&sb); return status; } } if (optind >= argc) { fprintf(stderr, "Missing interface argument\n"); goto usage; } ifmarker.target_range.min = NI_FSM_STATE_NONE; ifmarker.target_range.max = max_state; fsm->worker_timeout = ni_fsm_find_max_timeout(fsm, timeout); if (fsm->worker_timeout == NI_IFWORKER_INFINITE_TIMEOUT) ni_debug_application("wait for interfaces infinitely"); else ni_debug_application("wait %u seconds for interfaces", fsm->worker_timeout/1000); if (!ni_fsm_create_client(fsm)) { /* Severe error we always explicitly return */ return NI_WICKED_RC_ERROR; } if (!ni_fsm_refresh_state(fsm)) { /* Severe error we always explicitly return */ return NI_WICKED_RC_ERROR; } /* Get workers that match given criteria */ nmarked = 0; while (optind < argc) { ifmatch.name = argv[optind++]; ifmatch.ifdown = TRUE; ni_fsm_get_matching_workers(fsm, &ifmatch, &ifmarked); if (ni_string_eq(ifmatch.name, "all") || ni_string_empty(ifmatch.name)) { ni_string_array_destroy(&ifnames); break; } if (ni_string_array_index(&ifnames, ifmatch.name) == -1) ni_string_array_append(&ifnames, ifmatch.name); } /* Mark and start selected workers */ if (ifmarked.count) { if (ni_config_use_nanny()) { /* Disable devices and delete all related policies from nanny */ ni_ifdown_fire_nanny(&ifmarked); } /* Start workers to perform actual ifdown */ nmarked = ni_fsm_mark_matching_workers(fsm, &ifmarked, &ifmarker); } if (nmarked == 0) { ni_note("ifdown: no matching interfaces"); status = NI_WICKED_RC_SUCCESS; } else { if (ni_fsm_schedule(fsm) != 0) ni_fsm_mainloop(fsm); status = ni_ifstatus_shutdown_result(fsm, &ifnames, &ifmarked); } ni_string_array_destroy(&ifnames); ni_ifworker_array_destroy(&ifmarked); return status; }