123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- /*
- * syslog.c
- *
- *
- * Copyright 2015 Susi's Strolch
- *
- * For license information see projects "License.txt"
- *
- */
- #include <esp8266.h>
- #include "config.h"
- #include "syslog.h"
- #include "time.h"
- #include "task.h"
- #include "sntp.h"
- extern void * mem_trim(void *m, size_t s); // not well documented...
- #ifdef SYSLOG_DBG
- #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0)
- #else
- #define DBG(format, ...) do { } while(0)
- #endif
- #define WIFI_CHK_INTERVAL 1000 // ms to check Wifi statis
- static struct espconn *syslog_espconn = NULL;
- static uint32_t syslog_msgid = 1;
- static uint8_t syslog_task = 0;
- static syslog_host_t syslogHost;
- static syslog_entry_t *syslogQueue = NULL;
- static enum syslog_state syslogState = SYSLOG_NONE;
- static bool syslog_timer_armed = false;
- static void ICACHE_FLASH_ATTR syslog_add_entry(syslog_entry_t *entry);
- static void ICACHE_FLASH_ATTR syslog_chk_status(void);
- static void ICACHE_FLASH_ATTR syslog_udp_sent_cb(void *arg);
- static syslog_entry_t ICACHE_FLASH_ATTR *syslog_compose(uint8_t facility, uint8_t severity, const char *tag, const char *fmt, ...);
- #ifdef SYSLOG_UDP_RECV
- static void ICACHE_FLASH_ATTR syslog_udp_recv_cb(void *arg, char *pusrdata, unsigned short length);
- #endif
- #define syslog_send_udp() post_usr_task(syslog_task,0)
- static char ICACHE_FLASH_ATTR *syslog_get_status(void) {
- switch (syslogState)
- {
- case SYSLOG_NONE:
- return "SYSLOG_NONE";
- case SYSLOG_WAIT:
- return "SYSLOG_WAIT";
- case SYSLOG_INIT:
- return "SYSLOG_INIT";
- case SYSLOG_INITDONE:
- return "SYSLOG_INITDONE";
- case SYSLOG_DNSWAIT:
- return "SYSLOG_DNSWAIT";
- case SYSLOG_READY:
- return "SYSLOG_READY";
- case SYSLOG_SENDING:
- return "SYSLOG_SENDING";
- case SYSLOG_SEND:
- return "SYSLOG_SEND";
- case SYSLOG_SENT:
- return "SYSLOG_SENT";
- case SYSLOG_HALTED:
- return "SYSLOG_HALTED";
- case SYSLOG_ERROR:
- return "SYSLOG_ERROR";
- default:
- break;
- }
- return "UNKNOWN ";
- }
- static void ICACHE_FLASH_ATTR syslog_set_status(enum syslog_state state) {
- syslogState = state;
- DBG("[%dµs] %s: %s (%d)\n", WDEV_NOW(), __FUNCTION__, syslog_get_status(), state);
- #ifndef SYSLOG_DBG
- os_printf("Syslog state: %s\n", syslog_get_status());
- #endif
- }
- static void ICACHE_FLASH_ATTR syslog_timer_arm(int delay) {
- static os_timer_t wifi_chk_timer = {};
- syslog_timer_armed = true;
- os_timer_disarm(&wifi_chk_timer);
- os_timer_setfn(&wifi_chk_timer, (os_timer_func_t *)syslog_chk_status, NULL);
- os_timer_arm(&wifi_chk_timer, delay, 0);
- }
- /******************************************************************************
- * FunctionName : syslog_chk_status
- * Description : check whether get ip addr or not
- * Parameters : none
- * Returns : none
- *******************************************************************************/
- static void ICACHE_FLASH_ATTR syslog_chk_status(void)
- {
- struct ip_info ipconfig;
- DBG("[%uµs] %s: id=%lu ", WDEV_NOW(), __FUNCTION__, syslogQueue ? syslogQueue->msgid : 0);
- //disarm timer first
- syslog_timer_armed = false;
- //try to get ip info of ESP8266 station
- wifi_get_ip_info(STATION_IF, &ipconfig);
- int wifi_status = wifi_station_get_connect_status();
- if (wifi_status == STATION_GOT_IP && ipconfig.ip.addr != 0)
- {
- // it seems we have to add an additional delay after the Wifi is up and running.
- // so we simply add an intermediate state with 25ms delay
- switch (syslogState)
- {
- case SYSLOG_WAIT:
- DBG("%s: Wifi connected\n", syslog_get_status());
- syslog_set_status(SYSLOG_INIT);
- syslog_timer_arm(100);
- break;
- case SYSLOG_INIT:
- DBG("%s: init syslog\n", syslog_get_status());
- syslog_set_status(SYSLOG_INITDONE);
- syslog_init(flashConfig.syslog_host);
- syslog_timer_arm(10);
- break;
- case SYSLOG_DNSWAIT:
- DBG("%s: wait for DNS resolver\n", syslog_get_status());
- syslog_timer_arm(10);
- break;
- case SYSLOG_READY:
- DBG("%s: enforce sending\n", syslog_get_status());
- syslog_send_udp();
- break;
- case SYSLOG_SENDING:
- DBG("%s: delay\n", syslog_get_status());
- syslog_set_status(SYSLOG_SEND);
- syslog_timer_arm(2);
- break;
- case SYSLOG_SEND:
- DBG("%s: start sending\n", syslog_get_status());
- syslog_send_udp();
- break;
- default:
- DBG("%s: %d\n", syslog_get_status(), syslogState);
- break;
- }
- } else {
- if ((wifi_status == STATION_WRONG_PASSWORD ||
- wifi_status == STATION_NO_AP_FOUND ||
- wifi_status == STATION_CONNECT_FAIL)) {
- syslog_set_status(SYSLOG_ERROR);
- os_printf("*** connect failure!!!\n");
- } else {
- DBG("re-arming timer...\n");
- syslog_timer_arm(WIFI_CHK_INTERVAL);
- }
- }
- }
- /******************************************************************************
- * FunctionName : syslog_sent_cb
- * Description : udp sent successfully
- * fetch next syslog package, free old message
- * Parameters : arg -- Additional argument to pass to the callback function
- * Returns : none
- ******************************************************************************/
- static void ICACHE_FLASH_ATTR syslog_udp_sent_cb(void *arg)
- {
- struct espconn *pespconn = arg;
- (void) pespconn;
- DBG("[%uµs] %s: id=%lu\n", WDEV_NOW(), __FUNCTION__, syslogQueue ? syslogQueue->msgid : 0);
- // datagram is delivered - free and advance queue
- syslog_entry_t *pse = syslogQueue;
- syslogQueue = syslogQueue -> next;
- os_free(pse);
- if (syslogQueue == NULL)
- syslog_set_status(SYSLOG_READY);
- else {
- // UDP seems timecritical - we must ensure a minimum delay after each package...
- syslog_set_status(SYSLOG_SENDING);
- if (! syslog_timer_armed)
- syslog_chk_status();
- }
- }
- static void ICACHE_FLASH_ATTR
- syslog_udp_send_event(os_event_t *events) {
- // os_printf("syslog_udp_send_event: %d %lu, %lu\n", syslogState, syslogQueue->msgid, syslogQueue->tick);
- DBG("[%uµs] %s: id=%lu\n", WDEV_NOW(), __FUNCTION__, syslogQueue ? syslogQueue->msgid : 0);
- if (syslogQueue == NULL)
- syslog_set_status(SYSLOG_READY);
- else {
- int res = 0;
- syslog_espconn->proto.udp->remote_port = syslogHost.port; // ESP8266 udp remote port
- os_memcpy(syslog_espconn->proto.udp->remote_ip, &syslogHost.addr.addr, 4); // ESP8266 udp remote IP
- res = espconn_send(syslog_espconn, (uint8_t *)syslogQueue->datagram, syslogQueue->datagram_len);
- if (res != 0) {
- os_printf("syslog_udp_send: error %d\n", res);
- }
- }
- }
- /*****************************************************************************
- * FunctionName : syslog_recv_cb
- * Description : Processing the received udp packet
- * Parameters : arg -- Additional argument to pass to the callback function
- * pusrdata -- The received data (or NULL when the connection has been closed!)
- * length -- The length of received data
- * Returns : none
- ******************************************************************************/
- #ifdef SYSLOG_UDP_RECV
- static void ICACHE_FLASH_ATTR syslog_udp_recv_cb(void *arg, char *pusrdata, unsigned short length)
- {
- DBG("syslog_udp_recv_cb: %p, %p, %d\n", arg, pusrdata, length);
- }
- #endif
- /******************************************************************************
- *
- ******************************************************************************/
- static void ICACHE_FLASH_ATTR syslog_gethostbyname_cb(const char *name, ip_addr_t *ipaddr, void *arg)
- {
- struct espconn *pespconn = (struct espconn *)arg;
- (void) pespconn;
- DBG("[%uµs] %s\n", WDEV_NOW(), __FUNCTION__);
- if (ipaddr != NULL) {
- syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "SYSLOG",
- "resolved hostname: %s: " IPSTR, name, IP2STR(ipaddr));
- syslogHost.addr.addr = ipaddr->addr;
- syslog_set_status(SYSLOG_READY);
- } else {
- syslog_set_status(SYSLOG_ERROR);
- DBG("syslog_gethostbyname_cb: status=%s\n", syslog_get_status());
- }
- DBG("[%uµs] ex syslog_gethostbyname_cb()\n", WDEV_NOW());
- }
- /******************************************************************************
- * FunctionName : initSyslog
- * Description : Initialize the syslog library
- * Parameters : syslog_host -- the syslog host (host:port)
- * host: IP-Addr | hostname
- * Returns : none
- *******************************************************************************/
- void ICACHE_FLASH_ATTR syslog_init(char *syslog_host)
- {
- DBG("[%uµs] %s\n", WDEV_NOW(), __FUNCTION__);
- os_printf("SYSLOG host=%s *host=0x%x\n", syslog_host, *syslog_host);
- if (!*syslog_host) {
- syslog_set_status(SYSLOG_HALTED);
- return;
- }
- if (syslog_host == NULL) {
- // disable and unregister syslog handler
- syslog_set_status(SYSLOG_HALTED);
- if (syslog_espconn != NULL) {
- if (syslog_espconn->proto.udp) {
- // there's no counterpart to espconn_create...
- os_free(syslog_espconn->proto.udp);
- }
- os_free(syslog_espconn);
- }
- syslog_espconn = NULL;
- // clean up syslog queue
- syslog_entry_t *pse = syslogQueue;
- while (pse != NULL) {
- syslog_entry_t *next = pse->next;
- os_free(pse);
- pse = next;
- }
- syslogQueue = NULL;
- return;
- }
- char host[32], *port = &host[0];
- os_strncpy(host, syslog_host, 32);
- while (*port && *port != ':') // find port delimiter
- port++;
- if (*port) {
- *port++ = '\0';
- syslogHost.port = atoi(port);
- }
- if (syslogHost.port == 0)
- syslogHost.port = 514;
- // allocate structures, init syslog_handler
- if (syslog_espconn == NULL)
- syslog_espconn = (espconn *)os_zalloc(sizeof(espconn));
- if (syslog_espconn->proto.udp == NULL)
- syslog_espconn->proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp));
- syslog_espconn->type = ESPCONN_UDP;
- syslog_espconn->proto.udp->local_port = espconn_port(); // set a available port
- #ifdef SYSLOG_UDP_RECV
- espconn_regist_recvcb(syslog_espconn, syslog_udp_recv_cb); // register a udp packet receiving callback
- #endif
- espconn_regist_sentcb(syslog_espconn, syslog_udp_sent_cb); // register a udp packet sent callback
- syslog_task = register_usr_task(syslog_udp_send_event);
- syslogHost.min_heap_size = flashConfig.syslog_minheap;
- // the wifi_set_broadcast_if must be handled global in connection handler...
- // wifi_set_broadcast_if(STATIONAP_MODE); // send UDP broadcast from both station and soft-AP interface
- espconn_create(syslog_espconn); // create udp
- syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "SYSLOG",
- "syslogserver: %s:%d %d", host, syslogHost.port, syslog_espconn->proto.udp->local_port);
- if (UTILS_StrToIP((const char *)host, (void*)&syslogHost.addr)) {
- syslog_set_status(SYSLOG_READY);
- } else {
- syslog_set_status(SYSLOG_DNSWAIT);
- syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "SYSLOG",
- "must resolve hostname \"%s\"", host);
- espconn_gethostbyname(syslog_espconn, host, &syslogHost.addr, syslog_gethostbyname_cb);
- }
- }
- /******************************************************************************
- * FunctionName : syslog_add_entry
- * Description : add a syslog_entry_t to the syslogQueue
- * Parameters : entry: the syslog_entry_t
- * Returns : none
- ******************************************************************************/
- static void ICACHE_FLASH_ATTR
- syslog_add_entry(syslog_entry_t *entry)
- {
- syslog_entry_t *pse = syslogQueue;
- DBG("[%dµs] %s id=%lu\n", WDEV_NOW(), __FUNCTION__, entry->msgid);
- // append msg to syslog_queue
- if (pse == NULL)
- syslogQueue = entry;
- else {
- while (pse->next != NULL)
- pse = pse->next;
- pse->next = entry; // append msg to syslog queue
- }
- // DBG("%p %lu %d\n", entry, entry->msgid, system_get_free_heap_size());
- // ensure we have sufficient heap for the rest of the system
- if (system_get_free_heap_size() < syslogHost.min_heap_size) {
- if (syslogState != SYSLOG_HALTED) {
- os_printf("syslog_add_entry: Warning: queue filled up, halted\n");
- entry->next = syslog_compose(SYSLOG_FAC_USER, SYSLOG_PRIO_CRIT, "SYSLOG", "queue filled up, halted");
- if (syslogState == SYSLOG_READY)
- syslog_send_udp();
- syslog_set_status(SYSLOG_HALTED);
- }
- }
- }
- /******************************************************************************
- * FunctionName : syslog_compose
- * Description : compose a syslog_entry_t from va_args
- * Parameters : va_args
- * Returns : the malloced syslog_entry_t
- ******************************************************************************/
- LOCAL syslog_entry_t ICACHE_FLASH_ATTR *
- syslog_compose(uint8_t facility, uint8_t severity, const char *tag, const char *fmt, ...)
- {
- DBG("[%dµs] %s id=%lu\n", WDEV_NOW(), __FUNCTION__, syslog_msgid);
- syslog_entry_t *se = os_zalloc(sizeof (syslog_entry_t) + 1024); // allow up to 1k datagram
- if (se == NULL) return NULL;
- char *p = se->datagram;
- se->tick = WDEV_NOW(); // 0 ... 4294.967295s
- se->msgid = syslog_msgid;
- // The Priority value is calculated by first multiplying the Facility
- // number by 8 and then adding the numerical value of the Severity.
- p += os_sprintf(p, "<%d> ", facility * 8 + severity);
- // strftime doesn't work as expected - or adds 8k overhead.
- // so let's do poor man conversion - format is fixed anyway
- if (flashConfig.syslog_showdate == 0)
- p += os_sprintf(p, "- ");
- else {
- time_t now = NULL;
- struct tm *tp = NULL;
- // create timestamp: FULL-DATE "T" PARTIAL-TIME "Z": 'YYYY-mm-ddTHH:MM:SSZ '
- // as long as realtime_stamp is 0 we use tick div 10⁶ as date
- uint32_t realtime_stamp = sntp_get_current_timestamp();
- now = (realtime_stamp == 0) ? (se->tick / 1000000) : realtime_stamp;
- tp = gmtime(&now);
- p += os_sprintf(p, "%4d-%02d-%02dT%02d:%02d:%02d",
- tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday,
- tp->tm_hour, tp->tm_min, tp->tm_sec);
- if (realtime_stamp == 0)
- p += os_sprintf(p, ".%06uZ ", se->tick % 1000000);
- else
- p += os_sprintf(p, "%+03d:00 ", flashConfig.timezone_offset);
- }
- // add HOSTNAME APP-NAME PROCID MSGID
- if (flashConfig.syslog_showtick)
- p += os_sprintf(p, "%s %s %u.%06u %u ", flashConfig.hostname, tag, se->tick / 1000000,
- se->tick % 1000000, syslog_msgid++);
- else
- p += os_sprintf(p, "%s %s - %u ", flashConfig.hostname, tag, syslog_msgid++);
- // append syslog message
- va_list arglist;
- va_start(arglist, fmt);
- p += ets_vsprintf(p, fmt, arglist );
- va_end(arglist);
- se->datagram_len = p - se->datagram;
- se = mem_trim(se, sizeof(syslog_entry_t) + se->datagram_len + 1);
- return se;
- }
- /*****************************************************************************
- * FunctionName : syslog
- * Description : compose and queue a new syslog message
- * Parameters : facility
- * severity
- * tag
- * message
- * ...
- *
- * SYSLOG-MSG = HEADER SP STRUCTURED-DATA [SP MSG]
- HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME
- SP APP-NAME SP PROCID SP MSGID
- PRI = "<" PRIVAL ">"
- PRIVAL = 1*3DIGIT ; range 0 .. 191
- VERSION = NONZERO-DIGIT 0*2DIGIT
- HOSTNAME = NILVALUE / 1*255PRINTUSASCII
- APP-NAME = NILVALUE / 1*48PRINTUSASCII
- PROCID = NILVALUE / 1*128PRINTUSASCII
- MSGID = NILVALUE / 1*32PRINTUSASCII
- TIMESTAMP = NILVALUE / FULL-DATE "T" FULL-TIME
- FULL-DATE = DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY
- DATE-FULLYEAR = 4DIGIT
- DATE-MONTH = 2DIGIT ; 01-12
- DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
- ; month/year
- FULL-TIME = PARTIAL-TIME TIME-OFFSET
- PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND
- [TIME-SECFRAC]
- TIME-HOUR = 2DIGIT ; 00-23
- TIME-MINUTE = 2DIGIT ; 00-59
- TIME-SECOND = 2DIGIT ; 00-59
- TIME-SECFRAC = "." 1*6DIGIT
- TIME-OFFSET = "Z" / TIME-NUMOFFSET
- TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE
- STRUCTURED-DATA = NILVALUE / 1*SD-ELEMENT
- SD-ELEMENT = "[" SD-ID *(SP SD-PARAM) "]"
- SD-PARAM = PARAM-NAME "=" %d34 PARAM-VALUE %d34
- SD-ID = SD-NAME
- PARAM-NAME = SD-NAME
- PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and
- ; ']' MUST be escaped.
- SD-NAME = 1*32PRINTUSASCII
- ; except '=', SP, ']', %d34 (")
- MSG = MSG-ANY / MSG-UTF8
- MSG-ANY = *OCTET ; not starting with BOM
- MSG-UTF8 = BOM UTF-8-STRING
- BOM = %xEF.BB.BF
- UTF-8-STRING = *OCTET ; UTF-8 string as specified
- ; in RFC 3629
- OCTET = %d00-255
- SP = %d32
- PRINTUSASCII = %d33-126
- NONZERO-DIGIT = %d49-57
- DIGIT = %d48 / NONZERO-DIGIT
- NILVALUE = "-"
- *
- * TIMESTAMP: realtime_clock == 0 ? timertick / 10⁶ : realtime_clock
- * HOSTNAME hostname
- * APPNAME: ems-meta-id
- * PROCID: timertick
- * MSGID: NILVALUE
- *
- * Returns : none
- *******************************************************************************/
- void ICACHE_FLASH_ATTR syslog(uint8_t facility, uint8_t severity, const char *tag, const char *fmt, ...)
- {
- DBG("[%dµs] %s status: %s\n", WDEV_NOW(), __FUNCTION__, syslog_get_status());
- if (flashConfig.syslog_host[0] == 0 || syslogState == SYSLOG_ERROR || syslogState == SYSLOG_HALTED)
- return;
- if (severity > flashConfig.syslog_filter)
- return;
- // compose the syslog message
- void *arg = __builtin_apply_args();
- void *res = __builtin_apply((void*)syslog_compose, arg, 128);
- if (res == NULL) return; // compose failed, probably due to malloc failure
- syslog_entry_t *se = *(syslog_entry_t **)res;
- // and append it to the message queue
- syslog_add_entry(se);
- if (syslogState == SYSLOG_NONE)
- syslog_set_status(SYSLOG_WAIT);
- if (! syslog_timer_armed)
- syslog_chk_status();
- }
|