Example #1
0
void upsdrv_initinfo(void)
{
	char	*page, *last = NULL;
	char	buf[SMALLBUF];
	
	snprintf(buf, sizeof(buf), "%s", subdriver->initinfo);

	for (page = strtok_r(buf, " ", &last); page != NULL; page = strtok_r(NULL, " ", &last)) {

		if (netxml_get_page(page) != NE_OK) {
			continue;
		}

		dstate_setinfo("driver.version.data", "%s", subdriver->version);

		if (testvar("subscribe") && (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK)) {
			extrafd = ne_sock_fd(sock);
			time(&lastheard);
		}

		/* Register r/w variables */
		vname_register_rw();

		/* Set UPS driver handler callbacks */
		upsh.setvar  = &setvar;
		upsh.instcmd = &instcmd;

		return;
	}

	fatalx(EXIT_FAILURE, "%s: communication failure [%s]", __func__, ne_get_error(session));
}
Example #2
0
int reset_socket(ne_socket *sock)
{
#ifdef SO_LINGER
    /* Stevens' magic trick to send an RST on close(). */
    struct linger l = {1, 0};
    return setsockopt(ne_sock_fd(sock), SOL_SOCKET, SO_LINGER, &l, sizeof l);
#else
    return 1;
#endif
}
Example #3
0
/* close 'sock', performing lingering close to avoid premature RST. */
static int close_socket(ne_socket *sock)
{
#ifdef HAVE_SHUTDOWN
    char buf[20];
    int fd = ne_sock_fd(sock);

    shutdown(fd, 0);
    while (ne_sock_read(sock, buf, sizeof buf) > 0);
#endif
    return ne_sock_close(sock);
}
Example #4
0
static int netxml_alarm_subscribe(const char *page)
{
	int	ret, port = -1, secret = -1;
	char	buf[LARGEBUF], *s;
	ne_request	*request;
	ne_sock_addr	*addr;
	const ne_inet_addr	*ai;
	char	resp_buf[LARGEBUF];

	/* Clear response buffer */
	memset(resp_buf, 0, sizeof(resp_buf));

	upsdebugx(2, "%s: %s", __func__, page);

	sock = ne_sock_create();

	if (gethostname(buf, sizeof(buf)) == 0) {
		dstate_setinfo("driver.hostname", "%s", buf);
	} else {
		dstate_setinfo("driver.hostname", "<unknown>");
	}

#ifdef HAVE_NE_SOCK_CONNECT_TIMEOUT
	ne_sock_connect_timeout(sock, timeout);
#endif
	ne_sock_read_timeout(sock, 1);

	netxml_get_page(subdriver->configure);

	snprintf(buf, sizeof(buf),	"<?xml version=\"1.0\"?>\n");
	snprintfcat(buf, sizeof(buf),	"<Subscribe>\n");
	snprintfcat(buf, sizeof(buf),		"<Class>%s v%s</Class>\n", progname, DRIVER_VERSION);
	snprintfcat(buf, sizeof(buf),		"<Type>connected socket</Type>\n");
	snprintfcat(buf, sizeof(buf),		"<HostName>%s</HostName>\n", dstate_getinfo("driver.hostname"));
	snprintfcat(buf, sizeof(buf),		"<XMLClientParameters>\n");
	snprintfcat(buf, sizeof(buf),		"<ShutdownDuration>%d</ShutdownDuration>\n", shutdown_duration);
	if( shutdown_timer > 0 ) {
		snprintfcat(buf, sizeof(buf),	"<ShutdownTimer>%d</ShutdownTimer>\r\n", shutdown_timer);
	}
	else {
		snprintfcat(buf, sizeof(buf),	"<ShutdownTimer>NONE</ShutdownTimer>\n");
	}
	snprintfcat(buf, sizeof(buf),			"<AutoConfig>LOCAL</AutoConfig>\n");
	snprintfcat(buf, sizeof(buf),			"<OutletGroup>1</OutletGroup>\n");
	snprintfcat(buf, sizeof(buf),		"</XMLClientParameters>\n");
	snprintfcat(buf, sizeof(buf),		"<Warning></Warning>\n");
	snprintfcat(buf, sizeof(buf),	"</Subscribe>\n");

	/* now send subscription message setting all the proper flags */
	request = ne_request_create(session, "POST", page);
	ne_set_request_body_buffer(request, buf, strlen(buf));

	/* as the NMC reply is not xml standard compliant let's parse it this way */
	do {
#ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT
		alarm(timeout+1);
#endif
		ret = ne_begin_request(request);

#ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT
		alarm(0);
#endif
		if (ret != NE_OK) {
			break;
		}

		ret = ne_read_response_block(request, resp_buf, sizeof(resp_buf));

		if (ret == NE_OK) {
			ret = ne_end_request(request);
		}

	} while (ret == NE_RETRY);

	ne_request_destroy(request);

	/* due to different formats used by the various NMCs, we need to\
	   break up the reply in lines and parse each one separately */
	for (s = strtok(resp_buf, "\r\n"); s != NULL; s = strtok(NULL, "\r\n")) {
		upsdebugx(2, "%s: parsing %s", __func__, s);

		if (!strncasecmp(s, "<Port>", 6) && (sscanf(s+6, "%u", &port) != 1)) {
			return NE_RETRY;
		}

		if (!strncasecmp(s, "<Secret>", 8) && (sscanf(s+8, "%u", &secret) != 1)) {
			return NE_RETRY;
		}
	}

	if ((port == -1) || (secret == -1)) {
		upsdebugx(2, "%s: parsing initial subcription failed", __func__);
		return NE_RETRY;
	}

	/* Resolve the given hostname.  'flags' must be zero.  Hex
	* string IPv6 addresses (e.g. `::1') may be enclosed in brackets
	* (e.g. `[::1]'). */
	addr = ne_addr_resolve(uri.host, 0);

	/* Returns zero if name resolution was successful, non-zero on
	* error. */
	if (ne_addr_result(addr) != 0) {
		upsdebugx(2, "%s: name resolution failure on %s: %s", __func__, uri.host, ne_addr_error(addr, buf, sizeof(buf)));
		ne_addr_destroy(addr);
		return NE_RETRY;
	}

	for (ai = ne_addr_first(addr); ai != NULL; ai = ne_addr_next(addr)) {

		upsdebugx(2, "%s: connecting to host %s port %d", __func__, ne_iaddr_print(ai, buf, sizeof(buf)), port);

#ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT
		alarm(timeout+1);
#endif
		ret = ne_sock_connect(sock, ai, port);

#ifndef HAVE_NE_SOCK_CONNECT_TIMEOUT
		alarm(0);
#endif
		if (ret == NE_OK) {
			upsdebugx(2, "%s: connection to %s open on fd %d", __func__, uri.host, ne_sock_fd(sock));
			break;
		}
	}

	ne_addr_destroy(addr);

	if (ai == NULL) {
		upsdebugx(2, "%s: failed to create listening socket", __func__);
		return NE_RETRY;
	}

	snprintf(buf, sizeof(buf), "<Subscription Identification=\"%u\"></Subscription>", secret);
	ret = ne_sock_fullwrite(sock, buf, strlen(buf) + 1);

	if (ret != NE_OK) {
		upsdebugx(2, "%s: send failed: %s", __func__, ne_sock_error(sock));
		return NE_RETRY;
	}

	ret = ne_sock_read(sock, buf, sizeof(buf));

	if (ret < 1) {
		upsdebugx(2, "%s: read failed: %s", __func__, ne_sock_error(sock));
		return NE_RETRY;
	}

	if (strcasecmp(buf, "<Subscription Answer=\"ok\"></Subscription>")) {
		upsdebugx(2, "%s: subscription rejected", __func__);
		return NE_RETRY;
	}

	upslogx(LOG_INFO, "NSM connection to '%s' established", uri.host);
	return NE_OK;
}
Example #5
0
void upsdrv_updateinfo(void)
{
	int	ret, errors = 0;

	/* We really should be dealing with alarms through a separate callback, so that we can keep the
	 * processing of alarms and polling for data separated. Currently, this isn't supported by the
	 * driver main body, so we'll have to revert to polling each time we're called, unless the
	 * socket indicates we're no longer connected.
	 */
	if (testvar("subscribe")) {
		char	buf[LARGEBUF];

		ret = ne_sock_read(sock, buf, sizeof(buf));

		if (ret > 0) {
			/* alarm message received */

			ne_xml_parser	*parser = ne_xml_create();
			upsdebugx(2, "%s: ne_sock_read(%d bytes) => %s", __func__, ret, buf);
			ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL);
			ne_xml_parse(parser, buf, strlen(buf));
			ne_xml_destroy(parser);
			time(&lastheard);

		} else if ((ret == NE_SOCK_TIMEOUT) && (difftime(time(NULL), lastheard) < 180)) {
			/* timed out */

			upsdebugx(2, "%s: ne_sock_read(timeout)", __func__);

		} else {
			/* connection closed or unknown error */

			upslogx(LOG_ERR, "NSM connection with '%s' lost", uri.host);

			upsdebugx(2, "%s: ne_sock_read(%d) => %s", __func__, ret, ne_sock_error(sock));
			ne_sock_close(sock);

			if (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK) {
				extrafd = ne_sock_fd(sock);
				time(&lastheard);
				return;
			}

			dstate_datastale();
			extrafd = -1;
			return;
		}
	}

	/* get additional data */
	ret = netxml_get_page(subdriver->getobject);
	if (ret != NE_OK) {
		errors++;
	}

	ret = netxml_get_page(subdriver->summary);
	if (ret != NE_OK) {
		errors++;
	}

	if (errors > 1) {
		dstate_datastale();
		return;
	}

	status_init();

	alarm_init();
	netxml_alarm_set();
	alarm_commit();

	netxml_status_set();
	status_commit();

	dstate_dataok();
}