/* * Build a dbus-object encapsulating a network device. * If @server is non-NULL, register the object with a canonical object path */ static ni_dbus_object_t * __ni_objectmodel_build_autoip4_device_object(ni_dbus_server_t *server, ni_autoip_device_t *dev) { ni_dbus_object_t *object; char object_path[256]; if (dev->link.ifindex <= 0) { ni_error("%s: autoip4 device %s has bad ifindex %d", __func__, dev->ifname, dev->link.ifindex); return NULL; } if (server != NULL) { snprintf(object_path, sizeof(object_path), "Interface/%d", dev->link.ifindex); object = ni_dbus_server_register_object(server, object_path, &ni_objectmodel_autoip4dev_class, ni_autoip_device_get(dev)); } else { object = ni_dbus_object_new(&ni_objectmodel_autoip4dev_class, NULL, ni_autoip_device_get(dev)); } if (object == NULL) ni_fatal("Unable to create dbus object for autoip4 device %s", dev->ifname); ni_objectmodel_bind_compatible_interfaces(object); return object; }
/* * Register services and classes for dhcp4 supplicant service */ void ni_objectmodel_autoip4_init(void) { if (ni_objectmodel_init(NULL) == NULL) ni_fatal("Cannot initialize objectmodel, giving up."); ni_objectmodel_register_class(&ni_objectmodel_autoip4dev_class); ni_objectmodel_register_service(&ni_objectmodel_autoip4_service); }
int __ni_process_run(ni_process_t *pi, int *pfd) { const char *arg0 = pi->argv.data[0]; pid_t pid; if (pi->pid != 0) { ni_error("Cannot execute process instance twice (%s)", pi->process->command); return -1; } if (!ni_file_executable(arg0)) { ni_error("Unable to run %s; does not exist or is not executable", arg0); return -1; } signal(SIGCHLD, ni_process_sigchild); if ((pid = fork()) < 0) { ni_error("%s: unable to fork child process: %m", __func__); return -1; } pi->pid = pid; if (pid == 0) { int maxfd; int fd; if (chdir("/") < 0) ni_warn("%s: unable to chdir to /: %m", __func__); close(0); if ((fd = open("/dev/null", O_RDONLY)) < 0) ni_warn("%s: unable to open /dev/null: %m", __func__); else if (dup2(fd, 0) < 0) ni_warn("%s: cannot dup null descriptor: %m", __func__); if (pfd) { if (dup2(pfd[1], 1) < 0 || dup2(pfd[1], 2) < 0) ni_warn("%s: cannot dup pipe out descriptor: %m", __func__); } maxfd = getdtablesize(); for (fd = 3; fd < maxfd; ++fd) close(fd); /* NULL terminate argv and env lists */ ni_string_array_append(&pi->argv, NULL); ni_string_array_append(&pi->environ, NULL); arg0 = pi->argv.data[0]; execve(arg0, pi->argv.data, pi->environ.data); ni_fatal("%s: cannot execute %s: %m", __func__, arg0); } return 0; }
void get_string(ni_dbus_variant_t *dict, const char *name, const char *value) { if (strncmp(name, "default-", 8) != 0) ni_fatal("get_string: bad element name %s (should start with default-", name); if (value == NULL || *value == '\0') get_default(dict, name); else ni_dbus_dict_add_string(dict, name + 8, value); }
void * xrealloc(void *ptr, size_t size) { void *p = realloc(ptr, size); if (p == NULL) ni_fatal("allocation failed realloc(%p, %zu): %m", ptr, size); return p; }
void * xcalloc(unsigned int count, size_t size) { void *p = calloc(count, size); if (p == NULL) ni_fatal("allocation failed calloc(%u, %zu): %m", count, size); return p; }
/* * Alloc helpers with NULLL check */ void * xmalloc(size_t size) { void *p = malloc(size); if (p == NULL) ni_fatal("allocation failed malloc(%zu): %m", size); return p; }
char * xstrdup(const char *string) { char *p; if (string == NULL) return NULL; p = strdup(string); if (p == NULL) ni_fatal("allocation failed strdup(%s): %m", string); return p; }
const char * ni_config_backupdir(void) { ni_config_fslocation_t *fsloc = &ni_global.config->backupdir; static ni_bool_t firsttime = TRUE; if (firsttime) { if (ni_mkdir_maybe(fsloc->path, fsloc->mode) < 0) ni_fatal("Cannot create backup directory \"%s\": %m", fsloc->path); firsttime = FALSE; } return fsloc->path; }
/* * Open a file given O_* flags and permissions. */ static FILE * __ni_file_open(const char *pathname, const char *fmode, unsigned int flags, unsigned int permissions) { FILE *fp; int fd; if ((fd = open(pathname, flags, permissions)) < 0) { ni_error("unable to open file %s for %s: %m", pathname, (flags & O_ACCMODE) == O_RDONLY? "reading" : "writing"); return NULL; } switch (flags & O_ACCMODE) { case O_RDONLY: fmode = "r"; break; case O_WRONLY: fmode = (flags & O_APPEND)? "a" : "w"; break; case O_RDWR: fmode = (flags & O_APPEND)? "a+" : "w+"; break; default: ni_fatal("%s: bad open mode 0%o", __func__, flags & O_ACCMODE); } fp = fdopen(fd, fmode); if (fp == NULL) { ni_error("%s: fdopen(%d, %s) failed: %m", __func__, fd, fmode); close(fd); return NULL; } return fp; }
/* * background the current process */ int ni_daemonize(const char *pidfile, unsigned int permissions) { pid_t pid; if (pidfile) { /* check if service is already running */ pid = ni_pidfile_check(pidfile); if (pid < 0) return -1; if (pid > 0) { ni_error("cannot create pidfile %s: service already running", pidfile); return -1; } if (ni_file_exists(pidfile)) { if (unlink(pidfile) < 0) { ni_error("cannot remove stale pidfile %s: %m", pidfile); return -1; } ni_warn("removing stale pidfile %s", pidfile); } } if (pidfile && ni_pidfile_write(pidfile, permissions, getpid()) < 0) return -1; /* fork, chdir to root and close fds */ if (daemon(0, 0) < 0) ni_fatal("unable to background process! daemon() failed: %m"); if (pidfile) __ni_pidfile_write(pidfile, permissions, getpid(), 0); return 0; }
/* * Create the client and return the handle of the root object */ ni_dbus_object_t * ni_call_create_client(void) { static ni_dbus_object_t *root_object = NULL; if (root_object == NULL) { ni_dbus_client_t *client; ni_objectmodel_init(NULL); /* Use ni_objectmodel_create_client() */ client = ni_create_dbus_client(NI_OBJECTMODEL_DBUS_BUS_NAME); if (!client) ni_fatal("Unable to connect to wicked dbus service"); root_object = ni_dbus_client_object_new(client, &ni_dbus_anonymous_class, NI_OBJECTMODEL_OBJECT_PATH, NI_OBJECTMODEL_INTERFACE, NULL); } return root_object; }
int ni_dhcp4_tester_run(ni_dhcp4_tester_t *opts) { ni_netconfig_t *nc; ni_netdev_t *ifp = NULL; ni_dhcp4_device_t *dev = NULL; ni_dhcp4_request_t *req = NULL; unsigned int link_timeout = 20; int rv; if (opts->timeout && opts->timeout != -1U) { link_timeout = (opts->timeout * 2) / 3; opts->timeout -= link_timeout; } if (!opts || ni_string_empty(opts->ifname)) ni_fatal("Invalid start parameters!"); dhcp4_tester_opts = *opts; dhcp4_tester_status = NI_WICKED_RC_ERROR; if (!(nc = ni_global_state_handle(1))) ni_fatal("Cannot refresh interface list!"); if (!(ifp = ni_netdev_by_name(nc, opts->ifname))) ni_fatal("Cannot find interface with name '%s'", opts->ifname); if (!ni_dhcp4_supported(ifp)) ni_fatal("DHCPv4 not supported on '%s'", opts->ifname); if (!(dev = ni_dhcp4_device_new(ifp->name, &ifp->link))) ni_fatal("Cannot allocate dhcp4 client for '%s'", opts->ifname); ni_dhcp4_set_event_handler(ni_dhcp4_tester_protocol_event); if (!(req = ni_dhcp4_request_new())) { ni_error("Cannot allocate dhcp4 request"); goto failure; } if (!ni_dhcp4_tester_req_init(req, opts->request)) goto failure; if (!ni_netdev_link_is_up(ifp)) { ni_netdev_req_t *ifreq; ni_debug_dhcp("%s: Link is not up, trying to bring it up", ifp->name); ifreq = ni_netdev_req_new(); ifreq->ifflags = NI_IFF_LINK_UP | NI_IFF_NETWORK_UP; if ((rv = ni_system_interface_link_change(ifp, ifreq)) < 0) { ni_error("%s: Unable to set up link", ifp->name); ni_netdev_req_free(ifreq); goto failure; } ni_netdev_req_free(ifreq); do { sleep(1); if (!(nc = ni_global_state_handle(1))) goto failure; if (!(ifp = ni_netdev_by_index(nc, dev->link.ifindex))) break; if (ni_netdev_link_is_up(ifp)) break; ni_debug_dhcp("%s: Link is not (yet) up", ifp->name); } while (link_timeout-- > 1); if (!ifp || !ni_netdev_link_is_up(ifp) || !link_timeout) { ni_error("%s: Unable to bring link up", ifp && ifp->name ? ifp->name : dev->ifname); goto failure; } /* Do not try to send too early, even link is reported up now */ sleep(1); } if (opts->timeout && opts->timeout != -1U) req->acquire_timeout = opts->timeout; req->broadcast = opts->broadcast; if ((rv = ni_dhcp4_acquire(dev, req)) < 0) { ni_error("%s: DHCP4v6 acquire request %s failed: %s", dev->ifname, ni_uuid_print(&req->uuid), ni_strerror(rv)); goto failure; } dhcp4_tester_status = NI_WICKED_RC_IN_PROGRESS; while (!ni_caught_terminal_signal()) { long timeout; timeout = ni_timer_next_timeout(); if (ni_socket_wait(timeout) != 0) break; } ni_server_deactivate_interface_events(); ni_socket_deactivate_all(); failure: if (dev) ni_dhcp4_device_put(dev); if (req) ni_dhcp4_request_free(req); return dhcp4_tester_status; }
static int ni_do_ifup_nanny(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_ifworker_array_t ifmarked; ni_string_array_t opt_ifconfig = NI_STRING_ARRAY_INIT; ni_bool_t check_prio = TRUE, set_persistent = FALSE; ni_bool_t opt_transient = FALSE; int c, status = NI_WICKED_RC_USAGE; unsigned int timeout = 0; ni_fsm_t *fsm; fsm = ni_fsm_new(); ni_assert(fsm); ni_fsm_require_register_type("reachable", ni_ifworker_reachability_check_new); memset(&ifmatch, 0, sizeof(ifmatch)); 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; 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 { unsigned int sec; if (ni_parse_uint(optarg, &sec, 10) < 0) { ni_error("ifup: cannot parse timeout option \"%s\"", optarg); goto usage; } timeout = sec * 1000; /* sec -> msec */ } 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: set_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_client_create(fsm, &ifmarked) || !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_wait_for_interfaces) fsm->worker_timeout = ni_wait_for_interfaces; 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; } status = NI_WICKED_RC_SUCCESS; /* Get workers that match given criteria */ 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); } if (!ni_ifup_start_policies(&ifmarked, set_persistent)) status = NI_WICKED_RC_NOT_CONFIGURED; /* Wait for device-up events */ ni_timer_register(ni_wait_for_interfaces, ni_client_timer_expires, &status); while (status == NI_WICKED_RC_SUCCESS) { /* status is already success */ if (0 == ifmarked.count) break; if (ni_socket_wait(ni_wait_for_interfaces) != 0) ni_fatal("ni_socket_wait failed"); ni_timer_next_timeout(); } /* 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; }