X-Git-Url: https://the.earth.li/gitweb/?a=blobdiff_plain;f=mqtt-arp.c;h=024f44ccee9284f560a79f8794000f500fc37f26;hb=HEAD;hp=6c83ccacf87b107f0fe6503a83c5ff71f070056e;hpb=ce532d3405a9d7c45a7a9ab17a622c8703df5612;p=mqtt-arp.git diff --git a/mqtt-arp.c b/mqtt-arp.c index 6c83cca..024f44c 100644 --- a/mqtt-arp.c +++ b/mqtt-arp.c @@ -16,7 +16,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include +#include #include +#include #include #include #include @@ -37,6 +40,7 @@ #define MQTT_PORT 8883 #define MQTT_TOPIC "location/by-mac" #define LOCATION "home" +#define CONFIG_FILE "/etc/mqtt-arp.conf" /* How often (in seconds) to report that we see a device */ #define REPORT_INTERVAL (2 * 60) @@ -64,6 +68,12 @@ struct ma_config { }; bool debug = false; +bool want_shutdown = false; + +void shutdown_request(int signal) +{ + want_shutdown = true; +} bool mac_compare(uint8_t *a, uint8_t *b) { @@ -167,7 +177,7 @@ void main_loop(struct ma_config *config, struct mosquitto *mosq, int sock) hdr = (struct nlmsghdr *) buf; nd = (struct ndmsg *) (hdr + 1); - while (1) { + while (!want_shutdown) { received = recv(sock, buf, sizeof(buf), 0); if (debug) { t = time(NULL); @@ -190,19 +200,20 @@ void main_loop(struct ma_config *config, struct mosquitto *mosq, int sock) nd->ndm_type); } attr = (struct nlattr *) (nd + 1); - while (attr->nla_len > 0) { - data = (((uint8_t *) attr) + 4); + while (((uint8_t *) attr - buf) < hdr->nlmsg_len) { + data = (((uint8_t *) attr) + NLA_HDRLEN); if (attr->nla_type == NDA_LLADDR && nd->ndm_state == NUD_REACHABLE) { mqtt_mac_presence(config, mosq, data, true); } - attr = (struct nlattr *) - (((uint8_t *) attr) + attr->nla_len); + attr = (struct nlattr *) (((uint8_t *) attr) + + NLA_ALIGN(attr->nla_len)); } break; case RTM_DELNEIGH: case RTM_GETNEIGH: + break; default: printf("Unknown message type: %d\n", hdr->nlmsg_type); } @@ -275,14 +286,135 @@ int netlink_init(void) return sock; } +int read_config(char *file, struct ma_config *config, int *macs) +{ + FILE *f; + char line[256]; + int i; + + f = fopen(file, "r"); + if (f == NULL) { + fprintf(stderr, "Could not read config file %s\n", file); + return errno; + } + +#define INT_OPTION(opt, var) \ + if (strncmp(line, opt " ", sizeof(opt)) == 0) { \ + var = atoi(&line[sizeof(opt)]); \ + } +#define STRING_OPTION(opt, var) \ + if (strncmp(line, opt " ", sizeof(opt)) == 0) { \ + var = strdup(&line[sizeof(opt)]); \ + } + + while (fgets(line, sizeof(line), f) != NULL) { + for (i = strlen(line) - 1; i >= 0 && isspace(line[i]); i--) + line[i] = '\0'; + if (line[0] == '\0' || line[0] == '#') + continue; + + if (strncmp(line, "mac ", 4) == 0) { + if (*macs >= MAX_MACS) { + printf("Can only accept %d MAC addresses to" + " watch for.\n", MAX_MACS); + exit(EXIT_FAILURE); + } + sscanf(&line[4], + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &config->macs[*macs].mac[0], + &config->macs[*macs].mac[1], + &config->macs[*macs].mac[2], + &config->macs[*macs].mac[3], + &config->macs[*macs].mac[4], + &config->macs[*macs].mac[5]); + config->macs[*macs].valid = true; + (*macs)++; + } else + STRING_OPTION("mqtt_host", config->mqtt_host) else + INT_OPTION("mqtt_port", config->mqtt_port) else + STRING_OPTION("mqtt_user", config->mqtt_username) else + STRING_OPTION("mqtt_pass", config->mqtt_password) else + STRING_OPTION("mqtt_topic", config->mqtt_topic) else + STRING_OPTION("location", config->location) else + STRING_OPTION("capath", config->capath) + } + fclose(f); + + return 0; +} + +void override_config(const struct ma_config *source, struct ma_config *target) +{ + int i; + + if (source->mqtt_host != NULL) { + target->mqtt_host = source->mqtt_host; + } + if (source->mqtt_port != 0) { + target->mqtt_port = source->mqtt_port; + } + if (source->mqtt_username != NULL) { + target->mqtt_username = source->mqtt_username; + } + if (source->mqtt_password != NULL) { + target->mqtt_password = source->mqtt_password; + } + if (source->mqtt_topic != NULL) { + target->mqtt_topic = source->mqtt_topic; + } + if (source->location != NULL) { + target->location = source->location; + } + if (source->capath != NULL) { + target->capath = source->capath; + } + for (i = 0; i < MAX_MACS; ++i) { + if (source->macs[i].valid) { + memcpy(&target->macs[i], &source->macs[i], sizeof(struct mac_entry)); + } + } +} + +void print_config(const struct ma_config *config) +{ + int i, j; + + printf("Config:\n"); + printf("mqtt_host: %s\n", config->mqtt_host ? config->mqtt_host : "NULL"); + printf("mqtt_port: %d\n", config->mqtt_port); + printf("mqtt_username: %s\n", config->mqtt_username ? config->mqtt_username : "NULL"); + printf("mqtt_password: %s\n", config->mqtt_password ? config->mqtt_password : "NULL"); + printf("mqtt_topic: %s\n", config->mqtt_topic ? config->mqtt_topic : "NULL"); + printf("location: %s\n", config->location ? config->location : "NULL"); + printf("capath: %s\n", config->capath ? config->capath : "NULL"); + + for (i = 0; i < MAX_MACS; ++i) { + if (config->macs[i].valid) { + printf("macs[%d]: { valid: true, mac: ", i); + for (j = 0; j < 6; ++j) { + printf("%02x", config->macs[i].mac[j]); + if (j < 5) { + printf(":"); + } + } + printf("\n"); + } else { + printf("macs[%d]: { valid: false }\n", i); + } + } +} + struct option long_options[] = { { "capath", required_argument, 0, 'c' }, { "host", required_argument, 0, 'h' }, { "location", required_argument, 0, 'l' }, { "mac", required_argument, 0, 'm' }, + { "password", required_argument, 0, 'P' }, { "port", required_argument, 0, 'p' }, { "topic", required_argument, 0, 't' }, + { "username", required_argument, 0, 'u' }, { "verbose", no_argument, 0, 'v' }, + { "configfile", required_argument, 0, 'f' }, { 0, 0, 0, 0 } }; @@ -291,29 +423,34 @@ int main(int argc, char *argv[]) int sock; struct mosquitto *mosq; struct ma_config config; + struct ma_config cmdline_config; int option_index = 0; int macs = 0; - char c; + int c; + char *config_file = CONFIG_FILE; bzero(&config, sizeof(config)); + bzero(&cmdline_config, sizeof(cmdline_config)); config.mqtt_port = MQTT_PORT; while (1) { - c = getopt_long(argc, argv, "c:h:l:m:p:t:v", + c = getopt_long(argc, argv, "c:h:l:m:p:P:t:u:f:v", long_options, &option_index); if (c == -1) break; - switch (c) { + case 'f': + config_file = optarg; + break; case 'c': - config.capath = optarg; + cmdline_config.capath = optarg; break; case 'h': - config.mqtt_host = optarg; + cmdline_config.mqtt_host = optarg; break; case 'l': - config.location = optarg; + cmdline_config.location = optarg; break; case 'm': if (macs >= MAX_MACS) { @@ -323,18 +460,26 @@ int main(int argc, char *argv[]) } sscanf(optarg, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &config.macs[macs].mac[0], - &config.macs[macs].mac[1], - &config.macs[macs].mac[2], - &config.macs[macs].mac[3], - &config.macs[macs].mac[4], - &config.macs[macs].mac[5]); + &cmdline_config.macs[macs].mac[0], + &cmdline_config.macs[macs].mac[1], + &cmdline_config.macs[macs].mac[2], + &cmdline_config.macs[macs].mac[3], + &cmdline_config.macs[macs].mac[4], + &cmdline_config.macs[macs].mac[5]); + cmdline_config.macs[macs].valid = true; + macs++; break; case 'p': - config.mqtt_port = atoi(optarg); + cmdline_config.mqtt_port = atoi(optarg); + break; + case 'P': + cmdline_config.mqtt_password = optarg; break; case 't': - config.mqtt_topic = optarg; + cmdline_config.mqtt_topic = optarg; + break; + case 'u': + cmdline_config.mqtt_username = optarg; break; case 'v': debug = true; @@ -345,15 +490,30 @@ int main(int argc, char *argv[]) } } + read_config(config_file, &config, &macs); + + override_config(&cmdline_config, &config); + if (!config.mqtt_host) config.mqtt_host = MQTT_HOST; if (!config.mqtt_topic) - config.mqtt_host = MQTT_TOPIC; + config.mqtt_topic = MQTT_TOPIC; if (!config.location) - config.mqtt_host = LOCATION; + config.location = LOCATION; + + if (debug) + print_config(&config); + + signal(SIGTERM, shutdown_request); sock = netlink_init(); mosq = mqtt_init(&config); main_loop(&config, mosq, sock); + + mosquitto_disconnect(mosq); + mosquitto_loop_stop(mosq, true); + mosquitto_destroy(mosq); + mosquitto_lib_cleanup(); + close(sock); }