modules/logger.cc (103 lines of code) (raw):

/* logger.cc: Very simple example C++ netconsd module * * Copyright (C) 2016, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ #include <cerrno> #include <cstdio> #include <cstdlib> #include <cstring> #include <functional> #include <unordered_map> #include <inttypes.h> #include <fcntl.h> #include <netdb.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <msgbuf-struct.h> #include <ncrx-struct.h> #include <jhash.h> /* * The below allows us to index an unordered_map by an IP address. */ static bool operator==(const struct in6_addr& lhs, const struct in6_addr& rhs) { return std::memcmp(&lhs, &rhs, 16) == 0; } namespace std { template<> struct hash<struct in6_addr> { std::size_t operator()(struct in6_addr const& s) const { return jhash2((uint32_t*)&s, sizeof(s) / sizeof(uint32_t), 0xbeefdead); } }; } /* namespace std */ /* * Basic struct to hold the hostname and the FD for its logfile. */ struct logtarget { char hostname[INET6_ADDRSTRLEN + 1]; int fd; /* * Resolve the hostname, and open() an appropriately named file to * write the logs into. */ logtarget(struct in6_addr *src) { int ret; struct sockaddr_in6 sa = { .sin6_family = AF_INET6, .sin6_port = 0, }; memcpy(&sa.sin6_addr, src, sizeof(*src)); ret = getnameinfo((const struct sockaddr *)&sa, sizeof(sa), hostname, sizeof(hostname) - 1, NULL, 0, NI_NAMEREQD); if (ret) { const char *ptr; fprintf(stderr, "getnameinfo failed: %s\n", gai_strerror(ret)); ptr = inet_ntop(AF_INET6, src, hostname, INET6_ADDRSTRLEN); if (ptr == NULL) { fprintf(stderr, "inet_ntop failed: %s\n", strerror(errno)); snprintf(hostname, 8, "unknown"); } } ret = open(hostname, O_TRUNC|O_WRONLY|O_CREAT, 0644); if (ret == -1) { fprintf(stderr, "FATAL: open() failed: %m\n"); abort(); } fd = ret; } /* * Close the file */ ~logtarget(void) { close(fd); } }; /* * This relates the IP address of the remote host to its logtarget struct. */ static std::unordered_map<struct in6_addr, struct logtarget> *maps; /* * Return the existing logtarget struct if we've seen this host before; else, * initialize a new logtarget, insert it, and return that. */ static struct logtarget& get_target(int thread_nr, struct in6_addr *src) { auto itr = maps[thread_nr].find(*src); if (itr == maps[thread_nr].end()) return maps[thread_nr].emplace(*src, src).first->second; return itr->second; } /* * Actually write the line to the file */ static void write_log(struct logtarget& tgt, struct msg_buf *buf, struct ncrx_msg *msg) { if (!msg) dprintf(tgt.fd, "%s\n", buf->buf); else dprintf(tgt.fd, "%06" PRIu64 " %014" PRIu64 " %d %d %s%s%s%s%s\n", msg->seq, msg->ts_usec, msg->facility, msg->level, msg->cont_start ? "[CONT START] " : "", msg->cont ? "[CONT] " : "", msg->oos ? "[OOS] ": "", msg->seq_reset ? "[SEQ RESET] " : "", msg->text); } extern "C" int netconsd_output_init(int nr) { maps = new std::unordered_map<struct in6_addr, struct logtarget>[nr]; return 0; } extern "C" void netconsd_output_exit(void) { delete[] maps; } /* * This is the actual function called by netconsd. */ extern "C" void netconsd_output_handler(int t, struct in6_addr *src, struct msg_buf *buf, struct ncrx_msg *msg) { struct logtarget& cur = get_target(t, src); write_log(cur, buf, msg); }