コード例 #1
0
ファイル: addrconf.c プロジェクト: pwieczorkiewicz/wicked
static dbus_bool_t
ni_objectmodel_addrconf_static_drop(ni_dbus_object_t *object, unsigned int addrfamily,
			ni_dbus_message_t *reply, DBusError *error)
{
	ni_addrconf_lease_t *lease = NULL;
	ni_netdev_t *dev;
	int rv;

	if (!(dev = ni_objectmodel_unwrap_netif(object, error)))
		return FALSE;

	lease = ni_addrconf_lease_new(NI_ADDRCONF_STATIC, addrfamily);
	lease->state = NI_ADDRCONF_STATE_RELEASED;

	rv = __ni_system_interface_update_lease(dev, &lease);
	if (lease)
		ni_addrconf_lease_free(lease);

	if (rv < 0) {
		dbus_set_error(error,
				DBUS_ERROR_FAILED,
				"Error dropping static %s addresses: %s",
				ni_addrfamily_type_to_name(addrfamily),
				ni_strerror(rv));
		return FALSE;
	}

	/* Don't return anything */
	return TRUE;
}
コード例 #2
0
ファイル: addrconf.c プロジェクト: pwieczorkiewicz/wicked
/*
 * Generic lease properties
 */
static dbus_bool_t
__ni_objectmodel_addrconf_generic_get_lease(const ni_dbus_object_t *object,
				ni_addrconf_mode_t mode, unsigned int addrfamily,
				ni_dbus_variant_t *dict,
				DBusError *error)
{
	ni_netdev_t *dev;
	const ni_addrconf_lease_t *lease;

	if (!(dev = ni_objectmodel_unwrap_netif(object, error)))
		return FALSE;

#if 0
	NI_TRACE_ENTER_ARGS("dev=%s, af=%s, mode=%s", dev->name,
			ni_addrfamily_type_to_name(addrfamily),
			ni_addrconf_type_to_name(mode));
#endif
	if (!(lease = ni_netdev_get_lease(dev, addrfamily, mode)))
		return FALSE;

	ni_dbus_dict_add_uint32(dict, "state", lease->state);
	if (lease->flags)
		ni_dbus_dict_add_uint32(dict, "flags", lease->flags);
	if (!ni_uuid_is_null(&lease->uuid))
		ni_dbus_dict_add_uuid(dict,   "uuid", &lease->uuid);
	return TRUE;
}
コード例 #3
0
ファイル: addrconf.c プロジェクト: pwieczorkiewicz/wicked
/*
 * Forward an addrconf drop call to a supplicant service, such as DHCP or zeroconf
 */
static dbus_bool_t
ni_objectmodel_addrconf_forward_release(ni_dbus_addrconf_forwarder_t *forwarder,
			ni_netdev_t *dev, const ni_dbus_variant_t *dict,
			ni_dbus_message_t *reply, DBusError *error)
{
	ni_addrconf_lease_t *lease;
	dbus_bool_t rv;

	/* If we have no lease, neither pending nor granted, there's nothing we need to do.
	 */
	if ((lease = ni_netdev_get_lease(dev, forwarder->addrfamily, forwarder->addrconf)) == NULL)
		return TRUE;

	rv = ni_objectmodel_addrconf_forwarder_call(forwarder, dev, "drop", &lease->uuid, NULL, error);
	if (!rv) {
		switch (ni_dbus_get_error(error, NULL)) {
		case -NI_ERROR_ADDRCONF_NO_LEASE:
			ni_debug_objectmodel("%s: no %s/%s lease", dev->name,
				ni_addrconf_type_to_name(forwarder->addrconf),
				ni_addrfamily_type_to_name(forwarder->addrfamily));
			rv = TRUE;
			break;
		default:
			ni_debug_objectmodel("%s: service returned %s (%s)", forwarder->supplicant.interface,
				error->name, error->message);
		}
		return rv;
	}

	/* Check again whether we still have a lease for this. The addrconf supplicant may
	 * actually be fast, so that the callback has arrived before the reply to our original
	 * release() call. In that case, we would tell the client to wait for a release event
	 * that has already been broadcast (and ignored).
	 */
	if ((lease = ni_netdev_get_lease(dev, forwarder->addrfamily, forwarder->addrconf)) != NULL) {
		/* Tell the client to wait for an addressReleased event with the given uuid */
		ni_debug_objectmodel("%s/%s: found lease, waiting for drop notification from supplicant",
				ni_addrconf_type_to_name(forwarder->addrconf),
				ni_addrfamily_type_to_name(forwarder->addrfamily));
		rv =  __ni_objectmodel_return_callback_info(reply, NI_EVENT_ADDRESS_RELEASED, &lease->uuid, error);
	}
	return rv;
}
コード例 #4
0
ファイル: addrconf.c プロジェクト: pwieczorkiewicz/wicked
/*
 * Generic functions for static address configuration
 */
static dbus_bool_t
ni_objectmodel_addrconf_static_request(ni_dbus_object_t *object, unsigned int addrfamily,
			unsigned int argc, const ni_dbus_variant_t *argv,
			ni_dbus_message_t *reply, DBusError *error)
{
	ni_addrconf_lease_t *lease = NULL;
	const ni_dbus_variant_t *dict;
	const char *string_value;
	ni_netdev_t *dev;
	ni_address_t *ap;
	int rv;

	if (!(dev = ni_objectmodel_unwrap_netif(object, error)))
		return FALSE;

	if (argc != 1 || !ni_dbus_variant_is_dict(&argv[0])) {
		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
				"requestLease: expected one dict argument");
		return FALSE;
	}
	dict = &argv[0];

	lease = ni_addrconf_lease_new(NI_ADDRCONF_STATIC, addrfamily);
	lease->state = NI_ADDRCONF_STATE_GRANTED;
	ni_uuid_generate(&lease->uuid);

	if (!__ni_objectmodel_set_address_dict(&lease->addrs, dict, error)
	 || !__ni_objectmodel_set_route_dict(&lease->routes, dict, error)
	 || !__ni_objectmodel_set_resolver_dict(&lease->resolver, dict, error)) {
		ni_addrconf_lease_free(lease);
		return FALSE;
	}
	if (__ni_objectmodel_get_domain_string(dict, "hostname", &string_value))
		ni_string_dup(&lease->hostname, string_value);

	/* mark all addresses tentative, causing to verify them */
	for (ap = lease->addrs; ap; ap = ap->next)
		ni_address_set_tentative(ap, TRUE);

	rv = __ni_system_interface_update_lease(dev, &lease);
	if (lease)
		ni_addrconf_lease_free(lease);

	if (rv < 0) {
		dbus_set_error(error,
				DBUS_ERROR_FAILED,
				"Error configuring static %s addresses: %s",
				ni_addrfamily_type_to_name(addrfamily),
				ni_strerror(rv));
		return FALSE;
	}

	/* Don't return anything. */
	return TRUE;
}
コード例 #5
0
ファイル: leasefile.c プロジェクト: gsanso/wicked
static const char *
__ni_addrconf_lease_file_path(char **path, const char *dir,
		const char *ifname, int type, int family)
{
	const char *t = ni_addrconf_type_to_name(type);
	const char *f = ni_addrfamily_type_to_name(family);

	if (!path || ni_string_empty(dir) || ni_string_empty(ifname) || !t || !f)
		return NULL;
	return ni_string_printf(path, "%s/lease-%s-%s-%s.xml", dir, ifname, t, f);
}
コード例 #6
0
ファイル: leasefile.c プロジェクト: gsanso/wicked
int
__ni_addrconf_lease_info_to_xml(const ni_addrconf_lease_t *lease, xml_node_t *node)
{
	char hex[32] = { '\0' };

	xml_node_new_element("family", node, ni_addrfamily_type_to_name(lease->family));
	xml_node_new_element("type", node, ni_addrconf_type_to_name(lease->type));
	if (!ni_string_empty(lease->owner))
		xml_node_new_element("owner", node, lease->owner);
	if (!ni_uuid_is_null(&lease->uuid))
		xml_node_new_element("uuid", node, ni_uuid_print(&lease->uuid));
	xml_node_new_element("state", node, ni_addrconf_state_to_name(lease->state));
	snprintf(hex, sizeof(hex), "0x%08x", lease->update);
	xml_node_new_element("update", node, hex);
	xml_node_new_element_uint("acquired", node, lease->time_acquired);
	return 0;
}
コード例 #7
0
ファイル: compat.c プロジェクト: mchf/wicked
static xml_node_t *
__ni_compat_generate_static_address_list(xml_node_t *ifnode, ni_address_t *addr_list, unsigned int af)
{
	ni_address_t *ap;
	const char *afname;
	xml_node_t *aconf = NULL;

	afname = ni_addrfamily_type_to_name(af);
	if (!afname) {
		ni_error("%s: unknown address family %u", __func__, af);
		return NULL;
	}

	for (ap = addr_list; ap; ap = ap->next) {
		xml_node_t *anode;

		if (ap->family != af)
			continue;

		if (aconf == NULL) {
			char buffer[64];

			snprintf(buffer, sizeof(buffer), "%s:static", afname);
			aconf = xml_node_create(ifnode, buffer);
		}

		anode = xml_node_new("address", aconf);
		xml_node_new_element("local", anode, ni_sockaddr_prefix_print(&ap->local_addr, ap->prefixlen));

		if (ap->peer_addr.ss_family != AF_UNSPEC)
			xml_node_new_element("peer", anode, ni_sockaddr_print(&ap->peer_addr));
		if (ap->bcast_addr.ss_family != AF_UNSPEC)
			xml_node_new_element("broadcast", anode, ni_sockaddr_print(&ap->bcast_addr));
		if (ap->label)
			xml_node_new_element("label", anode, ap->label);
	}

	return aconf;
}
コード例 #8
0
ファイル: addrconf.c プロジェクト: pwieczorkiewicz/wicked
/*
 * Callback from addrconf supplicant whenever it acquired, released or lost a lease.
 *
 * FIXME SECURITY:
 * Is it good enough to check for the sender interface to avoid that someone is sending
 * us spoofed lease messages?!
 */
void
ni_objectmodel_addrconf_signal_handler(ni_dbus_connection_t *conn, ni_dbus_message_t *msg, void *user_data)
{
	ni_dbus_addrconf_forwarder_t *forwarder = user_data;
	const char *signal_name = dbus_message_get_member(msg);
	ni_netdev_t *ifp;
	ni_addrconf_lease_t *lease = NULL;
	ni_dbus_variant_t argv[16];
	ni_uuid_t uuid = NI_UUID_INIT;
	ni_event_t ifevent;
	int argc, optind = 0;

	memset(argv, 0, sizeof(argv));
	argc = ni_dbus_message_get_args_variants(msg, argv, 16);
	if (argc < 0) {
		ni_error("%s: cannot parse arguments for signal %s", __func__, signal_name);
		goto done;
	}

	ifp = ni_objectmodel_addrconf_path_to_device(dbus_message_get_path(msg));
	if (ifp == NULL) {
		ni_debug_dbus("%s: received signal %s for unknown interface %s", __func__,
				signal_name, dbus_message_get_path(msg));
		goto done;
	}

	lease = ni_addrconf_lease_new(forwarder->addrconf, forwarder->addrfamily);

	if (argc != 1 && argc != 2) {
		ni_warn("%s: ignoring %s event from %s: bad number of arguments (%u)",
				__func__, signal_name, dbus_message_get_path(msg), argc);
		goto done;
	}

	if (argc == 2 && !ni_dbus_variant_get_uuid(&argv[optind++], &uuid)) {
		ni_debug_dbus("%s: unable to parse uuid argument", __func__);
		goto done;
	}

	if (!ni_objectmodel_set_addrconf_lease(lease, &argv[optind++])) {
		ni_error("%s: unable to parse lease argument received from %s", __func__,
				dbus_message_get_sender(msg));
		goto done;
	}

	ni_debug_dbus("received signal %s for interface %s (ifindex %d), lease %s:%s, uuid=%s, update=0x%x, flags=0x%x",
			signal_name, ifp->name, ifp->link.ifindex,
			ni_addrfamily_type_to_name(lease->family),
			ni_addrconf_type_to_name(lease->type),
			ni_uuid_print(&uuid), lease->update, lease->flags);
	if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_ACQUIRED_SIGNAL)) {
		if (lease->state != NI_ADDRCONF_STATE_GRANTED) {
			ni_error("%s: unexpected lease state in signal %s", __func__, signal_name);
			goto done;
		}

		ifevent = NI_EVENT_ADDRESS_ACQUIRED;

		if (!__ni_addrconf_should_update(lease->update, NI_ADDRCONF_UPDATE_DEFAULT_ROUTE)) {
			ni_route_table_t *tab;
			ni_route_t *rp;
			unsigned int i;

			for (tab = lease->routes; tab; tab = tab->next) {
				for (i = 0; i < tab->routes.count; ++i) {
					if (!(rp = tab->routes.data[i]))
						continue;

					if (ni_sockaddr_is_specified(&rp->destination))
						continue;

					if (ni_route_array_delete(&tab->routes, i))
						i--;
				}
			}
		}
	} else if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_RELEASED_SIGNAL)) {
		lease->state = NI_ADDRCONF_STATE_RELEASED;
		ifevent = NI_EVENT_ADDRESS_RELEASED;
	} else if (!strcmp(signal_name, NI_OBJECTMODEL_LEASE_LOST_SIGNAL)) {
		lease->state = NI_ADDRCONF_STATE_FAILED;
		ifevent = NI_EVENT_ADDRESS_LOST;
	} else {
		/* Ignore unknown signal */
		goto done;
	}

	/*
	 * The following call updates the system with the information given in
	 * the lease. This includes setting all addresses, as well as updating
	 * resolver and hostname, if provided.
	 * When a lease is dropped, we either fall back to the config information
	 * from the next best lease, or if there is none, we restore the original
	 * system settings.
	 *
	 * Note, lease may be NULL after this, as the interface object
	 * takes ownership of it.
	 */
	__ni_system_interface_update_lease(ifp, &lease);

	/* Potentially, there's a client somewhere waiting for that event.
	 * We use the UUID that's passed back and forth to make sure we
	 * really match the event we were expecting to match.
	 */
	{
		ni_dbus_object_t *object;

		object = ni_objectmodel_get_netif_object(__ni_objectmodel_server, ifp);
		if (object)
			ni_objectmodel_send_netif_event(__ni_objectmodel_server, object,
					ifevent, ni_uuid_is_null(&uuid)? NULL : &uuid);
	}

done:
	while (argc--)
		ni_dbus_variant_destroy(&argv[argc]);
	if (lease)
		ni_addrconf_lease_free(lease);
}
コード例 #9
0
ファイル: leasefile.c プロジェクト: gsanso/wicked
/*
 * Write a lease to a file
 */
int
ni_addrconf_lease_file_write(const char *ifname, ni_addrconf_lease_t *lease)
{
	char tempname[PATH_MAX] = {'\0'};
	ni_bool_t fallback = FALSE;
	char *filename = NULL;
	xml_node_t *xml = NULL;
	FILE *fp = NULL;
	int ret = -1;
	int fd;

	if (lease->state == NI_ADDRCONF_STATE_RELEASED) {
		ni_addrconf_lease_file_remove(ifname, lease->type, lease->family);
		return 0;
	}

	if (!__ni_addrconf_lease_file_path(&filename, ni_config_storedir(),
					ifname, lease->type, lease->family)) {
		ni_error("Cannot construct lease file name: %m");
		return -1;
	}

	ni_debug_dhcp("Preparing xml lease data for '%s'", filename);
	if ((ret = ni_addrconf_lease_to_xml(lease, &xml)) != 0) {
		if (ret > 0) {
			ni_debug_dhcp("Skipped, %s:%s leases are disabled",
		                        ni_addrfamily_type_to_name(lease->family),
					ni_addrconf_type_to_name(lease->type));
		} else {
			ni_error("Unable to represent %s:%s lease as XML",
					ni_addrfamily_type_to_name(lease->family),
					ni_addrconf_type_to_name(lease->type));
		}
		goto failed;
	}

	snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename);
	if ((fd = mkstemp(tempname)) < 0) {
		if (errno == EROFS && __ni_addrconf_lease_file_path(&filename,
						ni_config_statedir(), ifname,
						lease->type, lease->family)) {
			ni_debug_dhcp("Read-only filesystem, try fallback to %s",
					filename);
			snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename);
			fd = mkstemp(tempname);
			fallback = TRUE;
		}
		if (fd < 0) {
			ni_error("Cannot create temporary lease file '%s': %m",
					tempname);
			tempname[0] = '\0';
			ret = -1;
			goto failed;
		}
	}
	if ((fp = fdopen(fd, "we")) == NULL) {
		ret = -1;
		close(fd);
		ni_error("Cannot reopen temporary lease file '%s': %m", tempname);
		goto failed;
	}

	ni_debug_dhcp("Writing lease to temporary file for '%s'", filename);
	xml_node_print(xml, fp);
	fclose(fp);
	xml_node_free(xml);

	if ((ret = rename(tempname, filename)) != 0) {
		ni_error("Unable to rename temporary lease file '%s' to '%s': %m",
				tempname, filename);
		goto failed;
	} else if (!fallback) {
		__ni_addrconf_lease_file_remove(ni_config_statedir(),
				ifname, lease->type, lease->family);
	}

	ni_debug_dhcp("Lease written to file '%s'", filename);
	ni_string_free(&filename);
	return 0;

failed:
	if (fp)
		fclose(fp);
	if (xml)
		xml_node_free(xml);
	if (tempname[0])
		unlink(tempname);
	ni_string_free(&filename);
	return -1;
}
コード例 #10
0
ファイル: update.c プロジェクト: mijos/wicked
/*
 * Remove information from a lease which has been released and already detached
 * from a device.
 */
static ni_bool_t
ni_system_updater_remove(ni_updater_t *updater, const ni_addrconf_lease_t *lease, const char *ifname)
{
	ni_string_array_t arguments = NI_STRING_ARRAY_INIT;
	ni_bool_t result = FALSE;

	ni_debug_ifconfig("Removing system %s settings from %s %s/%s lease",
			ni_updater_name(updater->kind), ifname,
			ni_addrconf_type_to_name(lease->type),
			ni_addrfamily_type_to_name(lease->family));

	if (!updater->proc_remove)
		return TRUE;

	ni_string_array_append(&arguments, "-i");
	ni_string_array_append(&arguments, ifname);

	ni_string_array_append(&arguments, "-t");
	ni_string_array_append(&arguments, ni_addrconf_type_to_name(lease->type));

	ni_string_array_append(&arguments, "-f");
	ni_string_array_append(&arguments, ni_addrfamily_type_to_name(lease->family));

	switch (updater->kind) {
	case NI_ADDRCONF_UPDATER_GENERIC:
		switch (updater->format) {
		case NI_ADDRCONF_UPDATER_FORMAT_INFO:
			ni_leaseinfo_remove(ifname, lease->type, lease->family);
			break;
		default:
			ni_error("Unsupported %s updater data format.",
				ni_updater_name(updater->kind));
			break;
		}
		break;

	case NI_ADDRCONF_UPDATER_RESOLVER:
	case NI_ADDRCONF_UPDATER_HOSTNAME:
		break;

	default:
		ni_error("cannot remove old %s settings - file format not understood",
				ni_updater_name(updater->kind));
		goto done;
	}

	if (!ni_system_updater_run(updater->proc_remove, &arguments)) {
		ni_error("failed to remove %s settings", ni_updater_name(updater->kind));
		goto done;
	}

	result = TRUE;

	switch (updater->kind) {
	case NI_ADDRCONF_UPDATER_RESOLVER:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_RESOLVER_UPDATED);
		break;

	case NI_ADDRCONF_UPDATER_HOSTNAME:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_HOSTNAME_UPDATED);
		break;

	case NI_ADDRCONF_UPDATER_GENERIC:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_GENERIC_UPDATED);
		break;

	default:
		break;
	}

done:
	ni_string_array_destroy(&arguments);
	return result;
}
コード例 #11
0
ファイル: update.c プロジェクト: mijos/wicked
/*
 * Install information from a lease, and remember that we did
 */
static ni_bool_t
ni_system_updater_install(ni_updater_t *updater, const ni_addrconf_lease_t *lease, const char *ifname)
{
	ni_string_array_t arguments = NI_STRING_ARRAY_INIT;
	const char *statedir = NULL;
	char *file = NULL;
	ni_bool_t result = FALSE;
	int rv = 0;

	ni_debug_ifconfig("Updating system %s settings from %s/%s lease",
					ni_updater_name(updater->kind),
					ni_addrconf_type_to_name(lease->type),
					ni_addrfamily_type_to_name(lease->family));

	if (!updater->proc_install)
		return TRUE;

	if (!ifname || (!updater->have_backup && !ni_system_updater_backup(updater, ifname)))
		return FALSE;

	ni_string_array_append(&arguments, "-i");
	ni_string_array_append(&arguments, ifname);

	ni_string_array_append(&arguments, "-t");
	ni_string_array_append(&arguments, ni_addrconf_type_to_name(lease->type));

	ni_string_array_append(&arguments, "-f");
	ni_string_array_append(&arguments, ni_addrfamily_type_to_name(lease->family));

	switch (updater->kind) {
	case NI_ADDRCONF_UPDATER_GENERIC:
		switch (updater->format) {
		case NI_ADDRCONF_UPDATER_FORMAT_INFO:
			ni_leaseinfo_dump(NULL, lease, ifname, NULL);
			if (!(file = ni_leaseinfo_path(ifname, lease->type, lease->family))) {
				ni_error("Unable to determine leaseinfo file path.");
				goto done;
			}
			ni_string_array_append(&arguments, file);
			break;

		default:
			ni_error("Unsupported %s updater data format.",
				ni_updater_name(updater->kind));
			goto done;
		}

		ni_string_array_append(&arguments,
				ni_updater_format_name(updater->format));
		break;

	case NI_ADDRCONF_UPDATER_RESOLVER:
		statedir = ni_extension_statedir(ni_updater_name(updater->kind));
		if (!statedir) {
			ni_error("failed to get %s statedir", ni_updater_name(updater->kind));
			goto done;
		}
		ni_string_printf(&file, "%s/resolv.conf.%s.%s.%s",
				statedir, ifname,
				ni_addrconf_type_to_name(lease->type),
				ni_addrfamily_type_to_name(lease->family));
		ni_string_array_append(&arguments, file);

		if ((rv = ni_resolver_write_resolv_conf(file, lease->resolver, NULL)) < 0) {
			ni_error("failed to write resolver info to file: %s",
					ni_strerror(rv));
			goto done;
		}
		break;

	case NI_ADDRCONF_UPDATER_HOSTNAME:
		if (!ni_string_empty(lease->hostname)) {
			ni_string_array_append(&arguments, lease->hostname);
		} else {
			const ni_address_t *ap;
			char *name = NULL;
			unsigned int count;

			/* bnc#861476 workaround */
			if (!can_try_reverse_lookup(lease))
				goto done;

			for (count = 0, ap = lease->addrs; ap; ap = ap->next) {
				if (!ni_sockaddr_is_specified(&ap->local_addr))
					continue;

				if (!ni_resolve_reverse_timed(&ap->local_addr,
						&name, NI_UPDATER_REVERSE_TIMEOUT))
					break;

				ni_info("Unable to resolve %s to hostname",
					ni_sockaddr_print(&ap->local_addr));

				if (++count >= NI_UPDATER_REVERSE_MAX_CNT)
					break;
			}

			if (ni_string_empty(name)) {
				ni_note("Skipping hostname update, none available");
				goto done;
			}
			ni_string_array_append(&arguments, name);
			ni_string_free(&name);
		}
		break;

	default:
		ni_error("cannot install new %s settings - file format not understood",
				ni_updater_name(updater->kind));
		goto done;
	}

	if (!ni_system_updater_run(updater->proc_install, &arguments)) {
		ni_error("failed to install %s settings", ni_updater_name(updater->kind));
		goto done;
	}

	result = TRUE;

	switch (updater->kind) {
	case NI_ADDRCONF_UPDATER_RESOLVER:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_RESOLVER_UPDATED);
		break;

	case NI_ADDRCONF_UPDATER_HOSTNAME:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_HOSTNAME_UPDATED);
		break;

	case NI_ADDRCONF_UPDATER_GENERIC:
		if (ni_global.other_event)
			ni_global.other_event(NI_EVENT_GENERIC_UPDATED);
		break;

	default:
		break;
	}

done:
	if (file)
		free(file);
	ni_string_array_destroy(&arguments);

	return result;
}