2018-02-27 00:34:50 +08:00
|
|
|
/**
|
|
|
|
* @file nsm.c
|
|
|
|
* @brief NSM client program
|
|
|
|
* @note Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "print.h"
|
|
|
|
#include "rtnl.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "version.h"
|
|
|
|
|
|
|
|
#define IFMT "\n\t\t"
|
|
|
|
#define NSM_NFD 3
|
|
|
|
|
|
|
|
struct nsm {
|
|
|
|
struct config *cfg;
|
|
|
|
struct fdarray fda;
|
|
|
|
struct transport *trp;
|
|
|
|
struct tsproc *tsproc;
|
|
|
|
struct ptp_message *nsm_delay_req;
|
|
|
|
struct ptp_message *nsm_delay_resp;
|
|
|
|
struct ptp_message *nsm_sync;
|
|
|
|
struct ptp_message *nsm_fup;
|
|
|
|
struct PortIdentity port_identity;
|
|
|
|
UInteger16 sequence_id;
|
|
|
|
const char *name;
|
|
|
|
} the_nsm;
|
|
|
|
|
|
|
|
static void nsm_help(FILE *fp);
|
|
|
|
static int nsm_request(struct nsm *nsm, char *target);
|
|
|
|
static void nsm_reset(struct nsm *nsm);
|
|
|
|
|
|
|
|
static int nsm_command(struct nsm *nsm, const char *cmd)
|
|
|
|
{
|
|
|
|
char action_str[10+1] = {0}, id_str[64+1] = {0};
|
|
|
|
|
|
|
|
if (0 == strncasecmp(cmd, "HELP", strlen(cmd))) {
|
|
|
|
nsm_help(stdout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (2 != sscanf(cmd, " %10s %64s", action_str, id_str)) {
|
|
|
|
pr_err("bad command: %s", cmd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (0 == strncasecmp(action_str, "NSM", strlen(action_str))) {
|
|
|
|
return nsm_request(nsm, id_str);
|
|
|
|
}
|
|
|
|
pr_err("bad command: %s", cmd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nsm_complete(struct nsm *nsm)
|
|
|
|
{
|
|
|
|
if (!nsm->nsm_sync) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (one_step(nsm->nsm_sync)) {
|
|
|
|
return nsm->nsm_delay_resp ? 1 : 0;
|
|
|
|
}
|
|
|
|
return (nsm->nsm_delay_resp && nsm->nsm_fup) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t nsm_compute_offset(struct tsproc *tsp,
|
|
|
|
struct ptp_message *syn,
|
|
|
|
struct ptp_message *fup,
|
|
|
|
struct ptp_message *req,
|
|
|
|
struct ptp_message *resp)
|
|
|
|
{
|
|
|
|
tmv_t c1, c2, c3, t1, t1c, t2, t3, t4, t4c, offset;
|
|
|
|
|
|
|
|
c1 = correction_to_tmv(syn->header.correction);
|
|
|
|
c2 = correction_to_tmv(fup->header.correction);
|
|
|
|
c3 = correction_to_tmv(resp->header.correction);
|
|
|
|
|
|
|
|
t1 = timestamp_to_tmv(fup->ts.pdu);
|
2018-03-12 20:36:30 +08:00
|
|
|
t2 = syn->hwts.ts;
|
|
|
|
t3 = req->hwts.ts;
|
2018-02-27 00:34:50 +08:00
|
|
|
t4 = timestamp_to_tmv(resp->ts.pdu);
|
|
|
|
|
|
|
|
t1c = tmv_add(t1, tmv_add(c1, c2));
|
|
|
|
t4c = tmv_sub(t4, c3);
|
|
|
|
|
|
|
|
tsproc_reset(tsp, 1);
|
|
|
|
tsproc_down_ts(tsp, t1c, t2);
|
|
|
|
tsproc_up_ts(tsp, t3, t4c);
|
|
|
|
tsproc_update_offset(tsp, &offset, NULL);
|
|
|
|
|
|
|
|
return tmv_to_nanoseconds(offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nsm_close(struct nsm *nsm)
|
|
|
|
{
|
|
|
|
nsm_reset(nsm);
|
|
|
|
transport_close(nsm->trp, &nsm->fda);
|
|
|
|
transport_destroy(nsm->trp);
|
|
|
|
tsproc_destroy(nsm->tsproc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nsm_handle_msg(struct nsm *nsm, struct ptp_message *msg, FILE *fp)
|
|
|
|
{
|
|
|
|
struct nsm_resp_tlv_head *head;
|
|
|
|
struct nsm_resp_tlv_foot *foot;
|
|
|
|
struct timePropertiesDS *tp;
|
|
|
|
struct PortAddress *paddr;
|
|
|
|
struct currentDS cds;
|
|
|
|
struct parentDS *pds;
|
|
|
|
struct Timestamp ts;
|
|
|
|
unsigned char *ptr;
|
|
|
|
int64_t offset;
|
|
|
|
|
|
|
|
if (!nsm->nsm_delay_req) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (msg->header.sequenceId !=
|
|
|
|
ntohs(nsm->nsm_delay_req->header.sequenceId)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-04-03 11:19:26 +08:00
|
|
|
if (!msg_unicast(msg)) {
|
2018-02-27 00:34:50 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (msg_type(msg)) {
|
|
|
|
case SYNC:
|
|
|
|
if (!nsm->nsm_sync) {
|
|
|
|
nsm->nsm_sync = msg;
|
|
|
|
msg_get(msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FOLLOW_UP:
|
|
|
|
if (!nsm->nsm_fup) {
|
|
|
|
nsm->nsm_fup = msg;
|
|
|
|
msg_get(msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DELAY_RESP:
|
|
|
|
if (!nsm->nsm_delay_resp) {
|
|
|
|
nsm->nsm_delay_resp = msg;
|
|
|
|
msg_get(msg);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DELAY_REQ:
|
|
|
|
case PDELAY_REQ:
|
|
|
|
case PDELAY_RESP:
|
|
|
|
case PDELAY_RESP_FOLLOW_UP:
|
|
|
|
case ANNOUNCE:
|
|
|
|
case SIGNALING:
|
|
|
|
case MANAGEMENT:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsm_complete(nsm)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
head = (struct nsm_resp_tlv_head *) nsm->nsm_delay_resp->delay_resp.suffix;
|
|
|
|
paddr = &head->parent_addr;
|
|
|
|
|
|
|
|
ptr = (unsigned char *) head;
|
|
|
|
ptr += sizeof(*head) + paddr->addressLength;
|
|
|
|
foot = (struct nsm_resp_tlv_foot *) ptr;
|
|
|
|
|
|
|
|
pds = &foot->parent;
|
|
|
|
memcpy(&cds, &foot->current, sizeof(cds));
|
|
|
|
tp = &foot->timeprop;
|
|
|
|
memcpy(&ts, &foot->lastsync, sizeof(ts));
|
|
|
|
|
|
|
|
offset = nsm_compute_offset(nsm->tsproc, nsm->nsm_sync, nsm->nsm_fup,
|
|
|
|
nsm->nsm_delay_req, nsm->nsm_delay_resp);
|
|
|
|
|
|
|
|
fprintf(fp, "NSM MEASUREMENT COMPLETE"
|
|
|
|
IFMT "offset %" PRId64
|
|
|
|
IFMT "portState %s"
|
|
|
|
IFMT "parentPortAddress %hu %s\n",
|
|
|
|
offset,
|
|
|
|
ps_str[head->port_state],
|
|
|
|
head->parent_addr.networkProtocol,
|
|
|
|
portaddr2str(&head->parent_addr));
|
|
|
|
fprintf(fp, "\tparentDataset"
|
|
|
|
IFMT "parentPortIdentity %s"
|
|
|
|
IFMT "parentStats %hhu"
|
|
|
|
IFMT "observedParentOffsetScaledLogVariance 0x%04hx"
|
|
|
|
IFMT "observedParentClockPhaseChangeRate 0x%08x"
|
|
|
|
IFMT "grandmasterPriority1 %hhu"
|
|
|
|
IFMT "gm.ClockClass %hhu"
|
|
|
|
IFMT "gm.ClockAccuracy 0x%02hhx"
|
|
|
|
IFMT "gm.OffsetScaledLogVariance 0x%04hx"
|
|
|
|
IFMT "grandmasterPriority2 %hhu"
|
|
|
|
IFMT "grandmasterIdentity %s\n",
|
|
|
|
pid2str(&pds->parentPortIdentity),
|
|
|
|
pds->parentStats,
|
|
|
|
pds->observedParentOffsetScaledLogVariance,
|
|
|
|
pds->observedParentClockPhaseChangeRate,
|
|
|
|
pds->grandmasterPriority1,
|
|
|
|
pds->grandmasterClockQuality.clockClass,
|
|
|
|
pds->grandmasterClockQuality.clockAccuracy,
|
|
|
|
pds->grandmasterClockQuality.offsetScaledLogVariance,
|
|
|
|
pds->grandmasterPriority2,
|
|
|
|
cid2str(&pds->grandmasterIdentity));
|
|
|
|
fprintf(fp, "\tcurrentDataset"
|
|
|
|
IFMT "stepsRemoved %hd"
|
|
|
|
IFMT "offsetFromMaster %.1f"
|
|
|
|
IFMT "meanPathDelay %.1f\n",
|
|
|
|
cds.stepsRemoved, cds.offsetFromMaster / 65536.0,
|
|
|
|
cds.meanPathDelay / 65536.0);
|
|
|
|
fprintf(fp, "\ttimePropertiesDataset"
|
|
|
|
IFMT "currentUtcOffset %hd"
|
|
|
|
IFMT "leap61 %d"
|
|
|
|
IFMT "leap59 %d"
|
|
|
|
IFMT "currentUtcOffsetValid %d"
|
|
|
|
IFMT "ptpTimescale %d"
|
|
|
|
IFMT "timeTraceable %d"
|
|
|
|
IFMT "frequencyTraceable %d"
|
|
|
|
IFMT "timeSource 0x%02hhx\n",
|
|
|
|
tp->currentUtcOffset,
|
|
|
|
tp->flags & LEAP_61 ? 1 : 0,
|
|
|
|
tp->flags & LEAP_59 ? 1 : 0,
|
|
|
|
tp->flags & UTC_OFF_VALID ? 1 : 0,
|
|
|
|
tp->flags & PTP_TIMESCALE ? 1 : 0,
|
|
|
|
tp->flags & TIME_TRACEABLE ? 1 : 0,
|
|
|
|
tp->flags & FREQ_TRACEABLE ? 1 : 0,
|
|
|
|
tp->timeSource);
|
|
|
|
fprintf(fp, "\tlastSyncTimestamp %" PRId64 ".%09u\n",
|
|
|
|
((uint64_t)ts.seconds_lsb) | (((uint64_t)ts.seconds_msb) << 32),
|
|
|
|
ts.nanoseconds);
|
|
|
|
|
|
|
|
fflush(fp);
|
|
|
|
nsm_reset(nsm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nsm_help(FILE *fp)
|
|
|
|
{
|
|
|
|
fprintf(fp, "\tSend a NetSync Monitor request to a specific port address:\n");
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
fprintf(fp, "\tNSM 111.222.333.444\n");
|
|
|
|
fprintf(fp, "\tNSM aa:bb:cc:dd:ee:ff\n");
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nsm_open(struct nsm *nsm, struct config *cfg)
|
|
|
|
{
|
|
|
|
enum transport_type transport;
|
2020-02-10 07:01:27 +08:00
|
|
|
char ts_label[IF_NAMESIZE];
|
2020-02-09 23:24:19 +08:00
|
|
|
const char *ifname, *name;
|
2018-02-27 00:34:50 +08:00
|
|
|
struct interface *iface;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
STAILQ_FOREACH(iface, &cfg->interfaces, list) {
|
2020-02-09 23:24:19 +08:00
|
|
|
ifname = interface_name(iface);
|
2020-02-10 07:01:27 +08:00
|
|
|
memset(ts_label, 0, sizeof(ts_label));
|
|
|
|
rtnl_get_ts_device(ifname, ts_label);
|
|
|
|
interface_set_label(iface, ts_label);
|
2020-02-10 05:50:53 +08:00
|
|
|
interface_ensure_tslabel(iface);
|
2018-02-27 00:34:50 +08:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (count != 1) {
|
|
|
|
pr_err("need exactly one interface");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iface = STAILQ_FIRST(&cfg->interfaces);
|
2020-02-09 23:24:19 +08:00
|
|
|
nsm->name = name = interface_name(iface);
|
2018-02-27 00:34:50 +08:00
|
|
|
nsm->cfg = cfg;
|
|
|
|
|
|
|
|
transport = config_get_int(cfg, name, "network_transport");
|
|
|
|
|
|
|
|
if (generate_clock_identity(&nsm->port_identity.clockIdentity, name)) {
|
|
|
|
pr_err("failed to generate a clock identity");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
nsm->port_identity.portNumber = 1;
|
|
|
|
|
|
|
|
nsm->tsproc = tsproc_create(TSPROC_RAW, FILTER_MOVING_AVERAGE, 10);
|
|
|
|
if (!nsm->tsproc) {
|
|
|
|
pr_err("failed to create time stamp processor");
|
|
|
|
goto no_tsproc;
|
|
|
|
}
|
|
|
|
nsm->trp = transport_create(cfg, transport);
|
|
|
|
if (!nsm->trp) {
|
|
|
|
pr_err("failed to create transport");
|
|
|
|
goto no_trans;
|
|
|
|
}
|
|
|
|
if (transport_open(nsm->trp, iface, &nsm->fda,
|
|
|
|
config_get_int(cfg, NULL, "time_stamping"))) {
|
|
|
|
pr_err("failed to open transport");
|
|
|
|
goto open_failed;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
open_failed:
|
|
|
|
transport_destroy(nsm->trp);
|
|
|
|
no_trans:
|
|
|
|
tsproc_destroy(nsm->tsproc);
|
|
|
|
no_tsproc:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ptp_message *nsm_recv(struct nsm *nsm, int fd)
|
|
|
|
{
|
|
|
|
struct ptp_message *msg;
|
|
|
|
int cnt, err;
|
|
|
|
|
|
|
|
msg = msg_allocate();
|
|
|
|
if (!msg) {
|
|
|
|
pr_err("low memory");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
msg->hwts.type = config_get_int(nsm->cfg, NULL, "time_stamping");
|
|
|
|
|
|
|
|
cnt = transport_recv(nsm->trp, fd, msg);
|
|
|
|
if (cnt <= 0) {
|
|
|
|
pr_err("recv message failed");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
err = msg_post_recv(msg, cnt);
|
|
|
|
if (err) {
|
|
|
|
switch (err) {
|
|
|
|
case -EBADMSG:
|
|
|
|
pr_err("bad message");
|
|
|
|
break;
|
|
|
|
case -EPROTO:
|
|
|
|
pr_debug("ignoring message");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto failed;
|
|
|
|
}
|
2018-04-16 22:28:19 +08:00
|
|
|
if (msg_sots_missing(msg)) {
|
|
|
|
pr_err("received %s without timestamp",
|
|
|
|
msg_type_string(msg_type(msg)));
|
|
|
|
goto failed;
|
|
|
|
}
|
2018-02-27 00:34:50 +08:00
|
|
|
|
|
|
|
return msg;
|
|
|
|
failed:
|
|
|
|
msg_put(msg);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nsm_request(struct nsm *nsm, char *target)
|
|
|
|
{
|
|
|
|
enum transport_type type = transport_type(nsm->trp);
|
|
|
|
UInteger8 transportSpecific;
|
|
|
|
struct ptp_message *msg;
|
|
|
|
struct tlv_extra *extra;
|
|
|
|
Integer64 asymmetry;
|
|
|
|
struct address dst;
|
|
|
|
int cnt, err;
|
|
|
|
|
2018-03-09 14:02:29 +08:00
|
|
|
if (str2addr(type, target, &dst)) {
|
2018-02-27 00:34:50 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = msg_allocate();
|
|
|
|
if (!msg) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
transportSpecific = config_get_int(nsm->cfg, nsm->name, "transportSpecific");
|
|
|
|
transportSpecific <<= 4;
|
|
|
|
|
|
|
|
asymmetry = config_get_int(nsm->cfg, nsm->name, "delayAsymmetry");
|
|
|
|
asymmetry <<= 16;
|
|
|
|
|
|
|
|
msg->hwts.type = config_get_int(nsm->cfg, NULL, "time_stamping");
|
|
|
|
|
|
|
|
msg->header.tsmt = DELAY_REQ | transportSpecific;
|
|
|
|
msg->header.ver = PTP_VERSION;
|
|
|
|
msg->header.messageLength = sizeof(struct delay_req_msg);
|
|
|
|
msg->header.domainNumber = config_get_int(nsm->cfg, NULL, "domainNumber");
|
|
|
|
msg->header.correction = -asymmetry;
|
|
|
|
msg->header.sourcePortIdentity = nsm->port_identity;
|
|
|
|
msg->header.sequenceId = nsm->sequence_id++;
|
|
|
|
msg->header.control = CTL_DELAY_REQ;
|
|
|
|
msg->header.logMessageInterval = 0x7f;
|
|
|
|
|
|
|
|
msg->address = dst;
|
|
|
|
msg->header.flagField[0] |= UNICAST;
|
|
|
|
|
|
|
|
extra = msg_tlv_append(msg, sizeof(struct TLV));
|
|
|
|
if (!extra) {
|
|
|
|
msg_put(msg);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
extra->tlv->type = TLV_PTPMON_REQ;
|
|
|
|
extra->tlv->length = 0;
|
|
|
|
|
|
|
|
err = msg_pre_send(msg);
|
|
|
|
if (err) {
|
|
|
|
pr_err("msg_pre_send failed");
|
|
|
|
goto out;
|
|
|
|
}
|
2018-03-17 14:35:09 +08:00
|
|
|
cnt = transport_sendto(nsm->trp, &nsm->fda, TRANS_EVENT, msg);
|
2018-02-27 00:34:50 +08:00
|
|
|
if (cnt <= 0) {
|
|
|
|
pr_err("transport_sendto failed");
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (msg_sots_missing(msg)) {
|
|
|
|
pr_err("missing timestamp on transmitted delay request");
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
nsm_reset(nsm);
|
|
|
|
nsm->nsm_delay_req = msg;
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
msg_put(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nsm_reset(struct nsm *nsm)
|
|
|
|
{
|
|
|
|
if (nsm->nsm_delay_req) {
|
|
|
|
msg_put(nsm->nsm_delay_req);
|
|
|
|
}
|
|
|
|
if (nsm->nsm_delay_resp) {
|
|
|
|
msg_put(nsm->nsm_delay_resp);
|
|
|
|
}
|
|
|
|
if (nsm->nsm_sync) {
|
|
|
|
msg_put(nsm->nsm_sync);
|
|
|
|
}
|
|
|
|
if (nsm->nsm_fup) {
|
|
|
|
msg_put(nsm->nsm_fup);
|
|
|
|
}
|
|
|
|
nsm->nsm_delay_req = NULL;
|
|
|
|
nsm->nsm_delay_resp = NULL;
|
|
|
|
nsm->nsm_sync = NULL;
|
|
|
|
nsm->nsm_fup = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usage(char *progname)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"\nusage: %s [options]\n\n"
|
|
|
|
" -f [file] read configuration from 'file'\n"
|
|
|
|
" -h prints this message and exits\n"
|
|
|
|
" -i [dev] interface device to use\n"
|
|
|
|
" -v prints the software version and exits\n"
|
|
|
|
"\n",
|
|
|
|
progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2018-04-19 19:43:23 +08:00
|
|
|
int batch_mode = 0, c, cnt, err = 0, index, length, tmo = -1;
|
2018-02-27 00:34:50 +08:00
|
|
|
char *cmd = NULL, *config = NULL, line[1024], *progname;
|
|
|
|
struct pollfd pollfd[NSM_NFD];
|
|
|
|
struct nsm *nsm = &the_nsm;
|
|
|
|
struct ptp_message *msg;
|
|
|
|
struct option *opts;
|
|
|
|
struct config *cfg;
|
|
|
|
|
|
|
|
if (handle_term_signals()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cfg = config_create();
|
|
|
|
if (!cfg) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
opts = config_long_options(cfg);
|
|
|
|
print_set_verbose(1);
|
|
|
|
print_set_syslog(0);
|
|
|
|
|
|
|
|
/* Process the command line arguments. */
|
|
|
|
progname = strrchr(argv[0], '/');
|
|
|
|
progname = progname ? 1+progname : argv[0];
|
|
|
|
while (EOF != (c = getopt_long(argc, argv, "f:hi:v", opts, &index))) {
|
|
|
|
switch (c) {
|
|
|
|
case 0:
|
|
|
|
if (config_parse_option(cfg, opts[index].name, optarg)) {
|
|
|
|
config_destroy(cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
config = optarg;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
if (!config_create_interface(optarg, cfg)) {
|
|
|
|
config_destroy(cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
version_show(stdout);
|
|
|
|
config_destroy(cfg);
|
|
|
|
return 0;
|
|
|
|
case 'h':
|
|
|
|
usage(progname);
|
|
|
|
config_destroy(cfg);
|
|
|
|
return 0;
|
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
usage(progname);
|
|
|
|
config_destroy(cfg);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
print_set_syslog(0);
|
|
|
|
print_set_verbose(1);
|
|
|
|
|
|
|
|
if (config && (err = config_read(config, cfg))) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
print_set_progname(progname);
|
|
|
|
print_set_tag(config_get_string(cfg, NULL, "message_tag"));
|
|
|
|
print_set_level(config_get_int(cfg, NULL, "logging_level"));
|
|
|
|
|
|
|
|
err = nsm_open(nsm, cfg);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
2018-04-19 19:43:23 +08:00
|
|
|
|
|
|
|
if (optind < argc) {
|
|
|
|
batch_mode = 1;
|
|
|
|
}
|
|
|
|
|
2018-02-27 00:34:50 +08:00
|
|
|
pollfd[0].fd = nsm->fda.fd[0];
|
|
|
|
pollfd[1].fd = nsm->fda.fd[1];
|
2018-04-19 19:43:23 +08:00
|
|
|
pollfd[2].fd = batch_mode ? -1 : STDIN_FILENO;
|
2018-02-27 00:34:50 +08:00
|
|
|
pollfd[0].events = POLLIN | POLLPRI;
|
|
|
|
pollfd[1].events = POLLIN | POLLPRI;
|
2018-04-19 19:43:23 +08:00
|
|
|
pollfd[2].events = batch_mode ? 0 : POLLIN | POLLPRI;
|
2018-02-27 00:34:50 +08:00
|
|
|
|
|
|
|
while (is_running()) {
|
2018-04-19 19:43:23 +08:00
|
|
|
if (batch_mode) {
|
|
|
|
if (optind < argc && !nsm->nsm_delay_req) {
|
|
|
|
cmd = argv[optind++];
|
|
|
|
if (nsm_command(nsm, cmd)) {
|
|
|
|
pr_err("command failed");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Wait a bit for any outstanding replies. */
|
|
|
|
tmo = 100;
|
|
|
|
}
|
|
|
|
|
2018-02-27 00:34:50 +08:00
|
|
|
cnt = poll(pollfd, NSM_NFD, tmo);
|
|
|
|
if (cnt < 0) {
|
|
|
|
if (EINTR == errno) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
pr_emerg("poll failed");
|
|
|
|
err = -1;
|
|
|
|
break;
|
|
|
|
}
|
2018-04-19 19:43:23 +08:00
|
|
|
} else if (!cnt && optind < argc) {
|
|
|
|
/* For batch mode. No response received from target node,
|
|
|
|
* continue with next command. */
|
|
|
|
nsm_reset(nsm);
|
|
|
|
continue;
|
2018-02-27 00:34:50 +08:00
|
|
|
} else if (!cnt) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (pollfd[2].revents & POLLHUP) {
|
|
|
|
if (tmo == -1) {
|
|
|
|
/* Wait a bit longer for outstanding replies. */
|
|
|
|
tmo = 100;
|
|
|
|
pollfd[2].fd = -1;
|
|
|
|
pollfd[2].events = 0;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pollfd[2].revents & (POLLIN|POLLPRI)) {
|
|
|
|
if (!fgets(line, sizeof(line), stdin)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
length = strlen(line);
|
|
|
|
if (length < 2) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
line[length - 1] = 0;
|
|
|
|
cmd = line;
|
|
|
|
if (nsm_command(nsm, cmd)) {
|
|
|
|
pr_err("command failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pollfd[0].revents & (POLLIN|POLLPRI)) {
|
|
|
|
msg = nsm_recv(nsm, pollfd[0].fd);
|
|
|
|
if (msg) {
|
|
|
|
nsm_handle_msg(nsm, msg, stdout);
|
|
|
|
msg_put(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pollfd[1].revents & (POLLIN|POLLPRI)) {
|
|
|
|
msg = nsm_recv(nsm, pollfd[1].fd);
|
|
|
|
if (msg) {
|
|
|
|
nsm_handle_msg(nsm, msg, stdout);
|
|
|
|
msg_put(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsm_close(nsm);
|
|
|
|
out:
|
|
|
|
msg_cleanup();
|
|
|
|
config_destroy(cfg);
|
|
|
|
return err;
|
|
|
|
}
|