From 3eb63d0f5a751d3bdf78b926ffc48bffeb919f59 Mon Sep 17 00:00:00 2001 From: wangxx1809 Date: Wed, 28 Feb 2024 10:20:51 +0800 Subject: [PATCH] first commit --- .gitignore | 10 + Include/Packet32.h | 359 +++ Include/Win32-Extensions.h | 113 + Include/bittypes.h | 137 + Include/ip6_misc.h | 163 ++ Include/pcap-bpf.h | 47 + Include/pcap-namedb.h | 42 + Include/pcap-stdinc.h | 93 + Include/pcap.h | 45 + Include/pcap/bluetooth.h | 48 + Include/pcap/bpf.h | 934 ++++++ Include/pcap/namedb.h | 89 + Include/pcap/pcap.h | 407 +++ Include/pcap/sll.h | 129 + Include/pcap/usb.h | 90 + Include/pcap/vlan.h | 46 + Include/remote-ext.h | 444 +++ Lib/Packet.lib | Bin 0 -> 8450 bytes Lib/libpacket.a | Bin 0 -> 20814 bytes Lib/libwpcap.a | Bin 0 -> 54276 bytes Lib/wpcap.lib | Bin 0 -> 19320 bytes Lib/x64/Packet.lib | Bin 0 -> 8290 bytes Lib/x64/wpcap.lib | Bin 0 -> 18892 bytes MyPcap.ico | Bin 0 -> 46227 bytes MyPcap.rc | Bin 0 -> 6578 bytes MyPcap.vcxproj | 167 ++ MyPcap.vcxproj.filters | 54 + MyPcap.vcxproj.user | 4 + Packet.cpp | 616 ++++ Packet.h | 168 ++ Resource.h | 30 + easylog/easylogging++.cc | 3116 ++++++++++++++++++++ easylog/easylogging++.h | 4572 ++++++++++++++++++++++++++++++ framework.h | 15 + main.cpp | 381 +++ output/Release/PDM客户端注销.BAT | 8 + output/Release/Packet.dll | Bin 0 -> 219600 bytes output/Release/install.bat | 8 + output/Release/notify.webp | Bin 0 -> 3704 bytes output/Release/uninstall.bat | 9 + output/Release/wpcap.dll | Bin 0 -> 489424 bytes small.ico | Bin 0 -> 46227 bytes targetver.h | 6 + utils/LocalAddr.h | 151 + wintoastlib.cpp | 1367 +++++++++ wintoastlib.h | 310 ++ 46 files changed, 14178 insertions(+) create mode 100644 .gitignore create mode 100644 Include/Packet32.h create mode 100644 Include/Win32-Extensions.h create mode 100644 Include/bittypes.h create mode 100644 Include/ip6_misc.h create mode 100644 Include/pcap-bpf.h create mode 100644 Include/pcap-namedb.h create mode 100644 Include/pcap-stdinc.h create mode 100644 Include/pcap.h create mode 100644 Include/pcap/bluetooth.h create mode 100644 Include/pcap/bpf.h create mode 100644 Include/pcap/namedb.h create mode 100644 Include/pcap/pcap.h create mode 100644 Include/pcap/sll.h create mode 100644 Include/pcap/usb.h create mode 100644 Include/pcap/vlan.h create mode 100644 Include/remote-ext.h create mode 100644 Lib/Packet.lib create mode 100644 Lib/libpacket.a create mode 100644 Lib/libwpcap.a create mode 100644 Lib/wpcap.lib create mode 100644 Lib/x64/Packet.lib create mode 100644 Lib/x64/wpcap.lib create mode 100644 MyPcap.ico create mode 100644 MyPcap.rc create mode 100644 MyPcap.vcxproj create mode 100644 MyPcap.vcxproj.filters create mode 100644 MyPcap.vcxproj.user create mode 100644 Packet.cpp create mode 100644 Packet.h create mode 100644 Resource.h create mode 100644 easylog/easylogging++.cc create mode 100644 easylog/easylogging++.h create mode 100644 framework.h create mode 100644 main.cpp create mode 100644 output/Release/PDM客户端注销.BAT create mode 100644 output/Release/Packet.dll create mode 100644 output/Release/install.bat create mode 100644 output/Release/notify.webp create mode 100644 output/Release/uninstall.bat create mode 100644 output/Release/wpcap.dll create mode 100644 small.ico create mode 100644 targetver.h create mode 100644 utils/LocalAddr.h create mode 100644 wintoastlib.cpp create mode 100644 wintoastlib.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9529b79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/x64/ +/.vs/ +/output/Debug +/output/Release/logs +/output/Release/*.log +/output/Release/*.pdb +/output/Release/*.exe +*.sln +/logs +/*.log \ No newline at end of file diff --git a/Include/Packet32.h b/Include/Packet32.h new file mode 100644 index 0000000..64be055 --- /dev/null +++ b/Include/Packet32.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2007 CACE Technologies, Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino, CACE Technologies + * nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @ingroup packetapi + * @{ + */ + +/** @defgroup packet32h Packet.dll definitions and data structures + * Packet32.h contains the data structures and the definitions used by packet.dll. + * The file is used both by the Win9x and the WinNTx versions of packet.dll, and can be included + * by the applications that use the functions of this library + * @{ + */ + +#ifndef __PACKET32 +#define __PACKET32 + +#include + +#ifdef HAVE_AIRPCAP_API +#include +#else +#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_) +#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ +typedef struct _AirpcapHandle *PAirpcapHandle; +#endif /* AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ */ +#endif /* HAVE_AIRPCAP_API */ + +#ifdef HAVE_DAG_API +#include +#endif /* HAVE_DAG_API */ + +// Working modes +#define PACKET_MODE_CAPT 0x0 ///< Capture mode +#define PACKET_MODE_STAT 0x1 ///< Statistical mode +#define PACKET_MODE_MON 0x2 ///< Monitoring mode +#define PACKET_MODE_DUMP 0x10 ///< Dump mode +#define PACKET_MODE_STAT_DUMP MODE_DUMP | MODE_STAT ///< Statistical dump Mode + + +/// Alignment macro. Defines the alignment size. +#define Packet_ALIGNMENT sizeof(int) +/// Alignment macro. Rounds up to the next even multiple of Packet_ALIGNMENT. +#define Packet_WORDALIGN(x) (((x)+(Packet_ALIGNMENT-1))&~(Packet_ALIGNMENT-1)) + +#define NdisMediumNull -1 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumCHDLC -2 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumPPPSerial -3 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumBare80211 -4 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumRadio80211 -5 ///< Custom linktype: NDIS doesn't provide an equivalent +#define NdisMediumPpi -6 ///< Custom linktype: NDIS doesn't provide an equivalent + +// Loopback behaviour definitions +#define NPF_DISABLE_LOOPBACK 1 ///< Drop the packets sent by the NPF driver +#define NPF_ENABLE_LOOPBACK 2 ///< Capture the packets sent by the NPF driver + +/*! + \brief Network type structure. + + This structure is used by the PacketGetNetType() function to return information on the current adapter's type and speed. +*/ +typedef struct NetType +{ + UINT LinkType; ///< The MAC of the current network adapter (see function PacketGetNetType() for more information) + ULONGLONG LinkSpeed; ///< The speed of the network in bits per second +}NetType; + + +//some definitions stolen from libpcap + +#ifndef BPF_MAJOR_VERSION + +/*! + \brief A BPF pseudo-assembly program. + + The program will be injected in the kernel by the PacketSetBPF() function and applied to every incoming packet. +*/ +struct bpf_program +{ + UINT bf_len; ///< Indicates the number of instructions of the program, i.e. the number of struct bpf_insn that will follow. + struct bpf_insn *bf_insns; ///< A pointer to the first instruction of the program. +}; + +/*! + \brief A single BPF pseudo-instruction. + + bpf_insn contains a single instruction for the BPF register-machine. It is used to send a filter program to the driver. +*/ +struct bpf_insn +{ + USHORT code; ///< Instruction type and addressing mode. + UCHAR jt; ///< Jump if true + UCHAR jf; ///< Jump if false + int k; ///< Generic field used for various purposes. +}; + +/*! + \brief Structure that contains a couple of statistics values on the current capture. + + It is used by packet.dll to return statistics about a capture session. +*/ +struct bpf_stat +{ + UINT bs_recv; ///< Number of packets that the driver received from the network adapter + ///< from the beginning of the current capture. This value includes the packets + ///< lost by the driver. + UINT bs_drop; ///< number of packets that the driver lost from the beginning of a capture. + ///< Basically, a packet is lost when the the buffer of the driver is full. + ///< In this situation the packet cannot be stored and the driver rejects it. + UINT ps_ifdrop; ///< drops by interface. XXX not yet supported + UINT bs_capt; ///< number of packets that pass the filter, find place in the kernel buffer and + ///< thus reach the application. +}; + +/*! + \brief Packet header. + + This structure defines the header associated with every packet delivered to the application. +*/ +struct bpf_hdr +{ + struct timeval bh_tstamp; ///< The timestamp associated with the captured packet. + ///< It is stored in a TimeVal structure. + UINT bh_caplen; ///< Length of captured portion. The captured portion can be different + ///< from the original packet, because it is possible (with a proper filter) + ///< to instruct the driver to capture only a portion of the packets. + UINT bh_datalen; ///< Original length of packet + USHORT bh_hdrlen; ///< Length of bpf header (this struct plus alignment padding). In some cases, + ///< a padding could be added between the end of this structure and the packet + ///< data for performance reasons. This filed can be used to retrieve the actual data + ///< of the packet. +}; + +/*! + \brief Dump packet header. + + This structure defines the header associated with the packets in a buffer to be used with PacketSendPackets(). + It is simpler than the bpf_hdr, because it corresponds to the header associated by WinPcap and libpcap to a + packet in a dump file. This makes straightforward sending WinPcap dump files to the network. +*/ +struct dump_bpf_hdr{ + struct timeval ts; ///< Time stamp of the packet + UINT caplen; ///< Length of captured portion. The captured portion can smaller than the + ///< the original packet, because it is possible (with a proper filter) to + ///< instruct the driver to capture only a portion of the packets. + UINT len; ///< Length of the original packet (off wire). +}; + + +#endif + +struct bpf_stat; + +#define DOSNAMEPREFIX TEXT("Packet_") ///< Prefix added to the adapters device names to create the WinPcap devices +#define MAX_LINK_NAME_LENGTH 64 //< Maximum length of the devices symbolic links +#define NMAX_PACKET 65535 + +/*! + \brief Addresses of a network adapter. + + This structure is used by the PacketGetNetInfoEx() function to return the IP addresses associated with + an adapter. +*/ +typedef struct npf_if_addr { + struct sockaddr_storage IPAddress; ///< IP address. + struct sockaddr_storage SubnetMask; ///< Netmask for that address. + struct sockaddr_storage Broadcast; ///< Broadcast address. +}npf_if_addr; + + +#define ADAPTER_NAME_LENGTH 256 + 12 ///< Maximum length for the name of an adapter. The value is the same used by the IP Helper API. +#define ADAPTER_DESC_LENGTH 128 ///< Maximum length for the description of an adapter. The value is the same used by the IP Helper API. +#define MAX_MAC_ADDR_LENGTH 8 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API. +#define MAX_NETWORK_ADDRESSES 16 ///< Maximum length for the link layer address of an adapter. The value is the same used by the IP Helper API. + + +typedef struct WAN_ADAPTER_INT WAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API +typedef WAN_ADAPTER *PWAN_ADAPTER; ///< Describes an opened wan (dialup, VPN...) network adapter using the NetMon API + +#define INFO_FLAG_NDIS_ADAPTER 0 ///< Flag for ADAPTER_INFO: this is a traditional ndis adapter +#define INFO_FLAG_NDISWAN_ADAPTER 1 ///< Flag for ADAPTER_INFO: this is a NdisWan adapter, and it's managed by WANPACKET +#define INFO_FLAG_DAG_CARD 2 ///< Flag for ADAPTER_INFO: this is a DAG card +#define INFO_FLAG_DAG_FILE 6 ///< Flag for ADAPTER_INFO: this is a DAG file +#define INFO_FLAG_DONT_EXPORT 8 ///< Flag for ADAPTER_INFO: when this flag is set, the adapter will not be listed or openend by winpcap. This allows to prevent exporting broken network adapters, like for example FireWire ones. +#define INFO_FLAG_AIRPCAP_CARD 16 ///< Flag for ADAPTER_INFO: this is an airpcap card +#define INFO_FLAG_NPFIM_DEVICE 32 + +/*! + \brief Describes an opened network adapter. + + This structure is the most important for the functioning of packet.dll, but the great part of its fields + should be ignored by the user, since the library offers functions that avoid to cope with low-level parameters +*/ +typedef struct _ADAPTER { + HANDLE hFile; ///< \internal Handle to an open instance of the NPF driver. + CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; ///< \internal A string containing the name of the network adapter currently opened. + int NumWrites; ///< \internal Number of times a packets written on this adapter will be repeated + ///< on the wire. + HANDLE ReadEvent; ///< A notification event associated with the read calls on the adapter. + ///< It can be passed to standard Win32 functions (like WaitForSingleObject + ///< or WaitForMultipleObjects) to wait until the driver's buffer contains some + ///< data. It is particularly useful in GUI applications that need to wait + ///< concurrently on several events. In Windows NT/2000 the PacketSetMinToCopy() + ///< function can be used to define the minimum amount of data in the kernel buffer + ///< that will cause the event to be signalled. + + UINT ReadTimeOut; ///< \internal The amount of time after which a read on the driver will be released and + ///< ReadEvent will be signaled, also if no packets were captured + CHAR Name[ADAPTER_NAME_LENGTH]; + PWAN_ADAPTER pWanAdapter; + UINT Flags; ///< Adapter's flags. Tell if this adapter must be treated in a different way, using the Netmon API or the dagc API. + +#ifdef HAVE_AIRPCAP_API + PAirpcapHandle AirpcapAd; +#endif // HAVE_AIRPCAP_API + +#ifdef HAVE_NPFIM_API + void* NpfImHandle; +#endif // HAVE_NPFIM_API + +#ifdef HAVE_DAG_API + dagc_t *pDagCard; ///< Pointer to the dagc API adapter descriptor for this adapter + PCHAR DagBuffer; ///< Pointer to the buffer with the packets that is received from the DAG card + struct timeval DagReadTimeout; ///< Read timeout. The dagc API requires a timeval structure + unsigned DagFcsLen; ///< Length of the frame check sequence attached to any packet by the card. Obtained from the registry + DWORD DagFastProcess; ///< True if the user requests fast capture processing on this card. Higher level applications can use this value to provide a faster but possibly unprecise capture (for example, libpcap doesn't convert the timestamps). +#endif // HAVE_DAG_API +} ADAPTER, *LPADAPTER; + +/*! + \brief Structure that contains a group of packets coming from the driver. + + This structure defines the header associated with every packet delivered to the application. +*/ +typedef struct _PACKET { + HANDLE hEvent; ///< \deprecated Still present for compatibility with old applications. + OVERLAPPED OverLapped; ///< \deprecated Still present for compatibility with old applications. + PVOID Buffer; ///< Buffer with containing the packets. See the PacketReceivePacket() for + ///< details about the organization of the data in this buffer + UINT Length; ///< Length of the buffer + DWORD ulBytesReceived; ///< Number of valid bytes present in the buffer, i.e. amount of data + ///< received by the last call to PacketReceivePacket() + BOOLEAN bIoComplete; ///< \deprecated Still present for compatibility with old applications. +} PACKET, *LPPACKET; + +/*! + \brief Structure containing an OID request. + + It is used by the PacketRequest() function to send an OID to the interface card driver. + It can be used, for example, to retrieve the status of the error counters on the adapter, its MAC address, + the list of the multicast groups defined on it, and so on. +*/ +struct _PACKET_OID_DATA { + ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h + ///< for a complete list of valid codes. + ULONG Length; ///< Length of the data field + UCHAR Data[1]; ///< variable-lenght field that contains the information passed to or received + ///< from the adapter. +}; +typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @} + */ + +/* +BOOLEAN QueryWinPcapRegistryStringA(CHAR *SubKeyName, + CHAR *Value, + UINT *pValueLen, + CHAR *DefaultVal); + +BOOLEAN QueryWinPcapRegistryStringW(WCHAR *SubKeyName, + WCHAR *Value, + UINT *pValueLen, + WCHAR *DefaultVal); +*/ + +//--------------------------------------------------------------------------- +// EXPORTED FUNCTIONS +//--------------------------------------------------------------------------- + +PCHAR PacketGetVersion(); +PCHAR PacketGetDriverVersion(); +BOOLEAN PacketSetMinToCopy(LPADAPTER AdapterObject,int nbytes); +BOOLEAN PacketSetNumWrites(LPADAPTER AdapterObject,int nwrites); +BOOLEAN PacketSetMode(LPADAPTER AdapterObject,int mode); +BOOLEAN PacketSetReadTimeout(LPADAPTER AdapterObject,int timeout); +BOOLEAN PacketSetBpf(LPADAPTER AdapterObject,struct bpf_program *fp); +BOOLEAN PacketSetLoopbackBehavior(LPADAPTER AdapterObject, UINT LoopbackBehavior); +INT PacketSetSnapLen(LPADAPTER AdapterObject,int snaplen); +BOOLEAN PacketGetStats(LPADAPTER AdapterObject,struct bpf_stat *s); +BOOLEAN PacketGetStatsEx(LPADAPTER AdapterObject,struct bpf_stat *s); +BOOLEAN PacketSetBuff(LPADAPTER AdapterObject,int dim); +BOOLEAN PacketGetNetType (LPADAPTER AdapterObject,NetType *type); +LPADAPTER PacketOpenAdapter(PCHAR AdapterName); +BOOLEAN PacketSendPacket(LPADAPTER AdapterObject,LPPACKET pPacket,BOOLEAN Sync); +INT PacketSendPackets(LPADAPTER AdapterObject,PVOID PacketBuff,ULONG Size, BOOLEAN Sync); +LPPACKET PacketAllocatePacket(void); +VOID PacketInitPacket(LPPACKET lpPacket,PVOID Buffer,UINT Length); +VOID PacketFreePacket(LPPACKET lpPacket); +BOOLEAN PacketReceivePacket(LPADAPTER AdapterObject,LPPACKET lpPacket,BOOLEAN Sync); +BOOLEAN PacketSetHwFilter(LPADAPTER AdapterObject,ULONG Filter); +BOOLEAN PacketGetAdapterNames(PTSTR pStr,PULONG BufferSize); +BOOLEAN PacketGetNetInfoEx(PCHAR AdapterName, npf_if_addr* buffer, PLONG NEntries); +BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); +HANDLE PacketGetReadEvent(LPADAPTER AdapterObject); +BOOLEAN PacketSetDumpName(LPADAPTER AdapterObject, void *name, int len); +BOOLEAN PacketSetDumpLimits(LPADAPTER AdapterObject, UINT maxfilesize, UINT maxnpacks); +BOOLEAN PacketIsDumpEnded(LPADAPTER AdapterObject, BOOLEAN sync); +BOOL PacketStopDriver(); +VOID PacketCloseAdapter(LPADAPTER lpAdapter); +BOOLEAN PacketStartOem(PCHAR errorString, UINT errorStringLength); +BOOLEAN PacketStartOemEx(PCHAR errorString, UINT errorStringLength, ULONG flags); +PAirpcapHandle PacketGetAirPcapHandle(LPADAPTER AdapterObject); + +// +// Used by PacketStartOemEx +// +#define PACKET_START_OEM_NO_NETMON 0x00000001 + +#ifdef __cplusplus +} +#endif + +#endif //__PACKET32 diff --git a/Include/Win32-Extensions.h b/Include/Win32-Extensions.h new file mode 100644 index 0000000..ad3be25 --- /dev/null +++ b/Include/Win32-Extensions.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2006 CACE Technologies, Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino, CACE Technologies + * nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __WIN32_EXTENSIONS_H__ +#define __WIN32_EXTENSIONS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Definitions */ + +/*! + \brief A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit(). +*/ +struct pcap_send_queue +{ + u_int maxlen; ///< Maximum size of the the queue, in bytes. This variable contains the size of the buffer field. + u_int len; ///< Current size of the queue, in bytes. + char *buffer; ///< Buffer containing the packets to be sent. +}; + +typedef struct pcap_send_queue pcap_send_queue; + +/*! + \brief This typedef is a support for the pcap_get_airpcap_handle() function +*/ +#if !defined(AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_) +#define AIRPCAP_HANDLE__EAE405F5_0171_9592_B3C2_C19EC426AD34__DEFINED_ +typedef struct _AirpcapHandle *PAirpcapHandle; +#endif + +#define BPF_MEM_EX_IMM 0xc0 +#define BPF_MEM_EX_IND 0xe0 + +/*used for ST*/ +#define BPF_MEM_EX 0xc0 +#define BPF_TME 0x08 + +#define BPF_LOOKUP 0x90 +#define BPF_EXECUTE 0xa0 +#define BPF_INIT 0xb0 +#define BPF_VALIDATE 0xc0 +#define BPF_SET_ACTIVE 0xd0 +#define BPF_RESET 0xe0 +#define BPF_SET_MEMORY 0x80 +#define BPF_GET_REGISTER_VALUE 0x70 +#define BPF_SET_REGISTER_VALUE 0x60 +#define BPF_SET_WORKING 0x50 +#define BPF_SET_ACTIVE_READ 0x40 +#define BPF_SET_AUTODELETION 0x30 +#define BPF_SEPARATION 0xff + +/* Prototypes */ +pcap_send_queue* pcap_sendqueue_alloc(u_int memsize); + +void pcap_sendqueue_destroy(pcap_send_queue* queue); + +int pcap_sendqueue_queue(pcap_send_queue* queue, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data); + +u_int pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue* queue, int sync); + +HANDLE pcap_getevent(pcap_t *p); + +struct pcap_stat *pcap_stats_ex(pcap_t *p, int *pcap_stat_size); + +int pcap_setuserbuffer(pcap_t *p, int size); + +int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks); + +int pcap_live_dump_ended(pcap_t *p, int sync); + +int pcap_offline_filter(struct bpf_program *prog, const struct pcap_pkthdr *header, const u_char *pkt_data); + +int pcap_start_oem(char* err_str, int flags); + +PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p); + +#ifdef __cplusplus +} +#endif + +#endif //__WIN32_EXTENSIONS_H__ diff --git a/Include/bittypes.h b/Include/bittypes.h new file mode 100644 index 0000000..558a0b5 --- /dev/null +++ b/Include/bittypes.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _BITTYPES_H +#define _BITTYPES_H + +#ifndef HAVE_U_INT8_T + +#if SIZEOF_CHAR == 1 +typedef unsigned char u_int8_t; +typedef signed char int8_t; +#elif SIZEOF_INT == 1 +typedef unsigned int u_int8_t; +typedef signed int int8_t; +#else /* XXX */ +#error "there's no appropriate type for u_int8_t" +#endif +#define HAVE_U_INT8_T 1 +#define HAVE_INT8_T 1 + +#endif /* HAVE_U_INT8_T */ + +#ifndef HAVE_U_INT16_T + +#if SIZEOF_SHORT == 2 +typedef unsigned short u_int16_t; +typedef signed short int16_t; +#elif SIZEOF_INT == 2 +typedef unsigned int u_int16_t; +typedef signed int int16_t; +#elif SIZEOF_CHAR == 2 +typedef unsigned char u_int16_t; +typedef signed char int16_t; +#else /* XXX */ +#error "there's no appropriate type for u_int16_t" +#endif +#define HAVE_U_INT16_T 1 +#define HAVE_INT16_T 1 + +#endif /* HAVE_U_INT16_T */ + +#ifndef HAVE_U_INT32_T + +#if SIZEOF_INT == 4 +typedef unsigned int u_int32_t; +typedef signed int int32_t; +#elif SIZEOF_LONG == 4 +typedef unsigned long u_int32_t; +typedef signed long int32_t; +#elif SIZEOF_SHORT == 4 +typedef unsigned short u_int32_t; +typedef signed short int32_t; +#else /* XXX */ +#error "there's no appropriate type for u_int32_t" +#endif +#define HAVE_U_INT32_T 1 +#define HAVE_INT32_T 1 + +#endif /* HAVE_U_INT32_T */ + +#ifndef HAVE_U_INT64_T +#if SIZEOF_LONG_LONG == 8 +typedef unsigned long long u_int64_t; +typedef long long int64_t; +#elif defined(_MSC_EXTENSIONS) +typedef unsigned _int64 u_int64_t; +typedef _int64 int64_t; +#elif SIZEOF_INT == 8 +typedef unsigned int u_int64_t; +#elif SIZEOF_LONG == 8 +typedef unsigned long u_int64_t; +#elif SIZEOF_SHORT == 8 +typedef unsigned short u_int64_t; +#else /* XXX */ +#error "there's no appropriate type for u_int64_t" +#endif + +#endif /* HAVE_U_INT64_T */ + +#ifndef PRId64 +#ifdef _MSC_EXTENSIONS +#define PRId64 "I64d" +#else /* _MSC_EXTENSIONS */ +#define PRId64 "lld" +#endif /* _MSC_EXTENSIONS */ +#endif /* PRId64 */ + +#ifndef PRIo64 +#ifdef _MSC_EXTENSIONS +#define PRIo64 "I64o" +#else /* _MSC_EXTENSIONS */ +#define PRIo64 "llo" +#endif /* _MSC_EXTENSIONS */ +#endif /* PRIo64 */ + +#ifndef PRIx64 +#ifdef _MSC_EXTENSIONS +#define PRIx64 "I64x" +#else /* _MSC_EXTENSIONS */ +#define PRIx64 "llx" +#endif /* _MSC_EXTENSIONS */ +#endif /* PRIx64 */ + +#ifndef PRIu64 +#ifdef _MSC_EXTENSIONS +#define PRIu64 "I64u" +#else /* _MSC_EXTENSIONS */ +#define PRIu64 "llu" +#endif /* _MSC_EXTENSIONS */ +#endif /* PRIu64 */ + +#endif /* _BITTYPES_H */ diff --git a/Include/ip6_misc.h b/Include/ip6_misc.h new file mode 100644 index 0000000..562fa61 --- /dev/null +++ b/Include/ip6_misc.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1993, 1994, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#) $Header: /tcpdump/master/libpcap/Win32/Include/ip6_misc.h,v 1.5 2006-01-22 18:02:18 gianluca Exp $ (LBL) + */ + +/* + * This file contains a collage of declarations for IPv6 from FreeBSD not present in Windows + */ + +#include + +#include + +#ifndef __MINGW32__ +#define IN_MULTICAST(a) IN_CLASSD(a) +#endif + +#define IN_EXPERIMENTAL(a) ((((u_int32_t) (a)) & 0xf0000000) == 0xf0000000) + +#define IN_LOOPBACKNET 127 + +#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF) +/* IPv6 address */ +struct in6_addr + { + union + { + u_int8_t u6_addr8[16]; + u_int16_t u6_addr16[8]; + u_int32_t u6_addr32[4]; + } in6_u; +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +#define s6_addr64 in6_u.u6_addr64 + }; + +#define IN6ADDR_ANY_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } +#define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } +#endif /* __MINGW32__ */ + + +#if (defined _MSC_VER) || (defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF)) +typedef unsigned short sa_family_t; +#endif + + +#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF) + +#define __SOCKADDR_COMMON(sa_prefix) \ + sa_family_t sa_prefix##family + +/* Ditto, for IPv6. */ +struct sockaddr_in6 + { + __SOCKADDR_COMMON (sin6_); + u_int16_t sin6_port; /* Transport layer port # */ + u_int32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + }; + +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((((u_int32_t *) (a))[0] == 0) && (((u_int32_t *) (a))[1] == 0) && \ + (((u_int32_t *) (a))[2] == htonl (0xffff))) + +#define IN6_IS_ADDR_MULTICAST(a) (((u_int8_t *) (a))[0] == 0xff) + +#define IN6_IS_ADDR_LINKLOCAL(a) \ + ((((u_int32_t *) (a))[0] & htonl (0xffc00000)) == htonl (0xfe800000)) + +#define IN6_IS_ADDR_LOOPBACK(a) \ + (((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \ + ((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1)) +#endif /* __MINGW32__ */ + +#define ip6_vfc ip6_ctlun.ip6_un2_vfc +#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow +#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen +#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt +#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim +#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim + +#define nd_rd_type nd_rd_hdr.icmp6_type +#define nd_rd_code nd_rd_hdr.icmp6_code +#define nd_rd_cksum nd_rd_hdr.icmp6_cksum +#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0] + +/* + * IPV6 extension headers + */ +#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */ +#define IPPROTO_IPV6 41 /* IPv6 header. */ +#define IPPROTO_ROUTING 43 /* IPv6 routing header */ +#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */ +#define IPPROTO_ESP 50 /* encapsulating security payload */ +#define IPPROTO_AH 51 /* authentication header */ +#define IPPROTO_ICMPV6 58 /* ICMPv6 */ +#define IPPROTO_NONE 59 /* IPv6 no next header */ +#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */ +#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */ + +#define IPV6_RTHDR_TYPE_0 0 + +/* Option types and related macros */ +#define IP6OPT_PAD1 0x00 /* 00 0 00000 */ +#define IP6OPT_PADN 0x01 /* 00 0 00001 */ +#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */ +#define IP6OPT_JUMBO_LEN 6 +#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */ + +#define IP6OPT_RTALERT_LEN 4 +#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */ +#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */ +#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */ +#define IP6OPT_MINLEN 2 + +#define IP6OPT_BINDING_UPDATE 0xc6 /* 11 0 00110 */ +#define IP6OPT_BINDING_ACK 0x07 /* 00 0 00111 */ +#define IP6OPT_BINDING_REQ 0x08 /* 00 0 01000 */ +#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */ +#define IP6OPT_EID 0x8a /* 10 0 01010 */ + +#define IP6OPT_TYPE(o) ((o) & 0xC0) +#define IP6OPT_TYPE_SKIP 0x00 +#define IP6OPT_TYPE_DISCARD 0x40 +#define IP6OPT_TYPE_FORCEICMP 0x80 +#define IP6OPT_TYPE_ICMP 0xC0 + +#define IP6OPT_MUTABLE 0x20 + + +#if defined(__MINGW32__) && defined(DEFINE_ADDITIONAL_IPV6_STUFF) +#ifndef EAI_ADDRFAMILY +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif +#endif /* __MINGW32__ */ diff --git a/Include/pcap-bpf.h b/Include/pcap-bpf.h new file mode 100644 index 0000000..5fe129d --- /dev/null +++ b/Include/pcap-bpf.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap-bpf.h,v 1.50 2007/04/01 21:43:55 guy Exp $ (LBL) + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Some applications + * might expect to be able to include . + */ +#include diff --git a/Include/pcap-namedb.h b/Include/pcap-namedb.h new file mode 100644 index 0000000..80a2f00 --- /dev/null +++ b/Include/pcap-namedb.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap-namedb.h,v 1.13 2006/10/04 18:13:32 guy Exp $ (LBL) + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Some applications + * might expect to be able to include . + */ +#include diff --git a/Include/pcap-stdinc.h b/Include/pcap-stdinc.h new file mode 100644 index 0000000..e373666 --- /dev/null +++ b/Include/pcap-stdinc.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2002 - 2005 NetGroup, Politecnico di Torino (Italy) + * Copyright (c) 2005 - 2009 CACE Technologies, Inc. Davis (California) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap-stdinc.h,v 1.10.2.1 2008-10-06 15:38:39 gianluca Exp $ (LBL) + */ + +#define SIZEOF_CHAR 1 +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 +#ifndef _MSC_EXTENSIONS +#define SIZEOF_LONG_LONG 8 +#endif + +/* + * Avoids a compiler warning in case this was already defined + * (someone defined _WINSOCKAPI_ when including 'windows.h', in order + * to prevent it from including 'winsock.h') + */ +#ifdef _WINSOCKAPI_ +#undef _WINSOCKAPI_ +#endif +#include + +#include + +#include "bittypes.h" +#include +#include + +#ifndef __MINGW32__ +#include "IP6_misc.h" +#endif + +#define caddr_t char* + +#if _MSC_VER < 1500 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strdup _strdup +#endif + +//#define inline __inline + +#ifdef __MINGW32__ +#include +#else /*__MINGW32__*/ +/* MSVC compiler */ +#ifndef _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef _W64 unsigned int uintptr_t; +#endif +#define _UINTPTR_T_DEFINED +#endif + +#ifndef _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef __int64 intptr_t; +#else +typedef _W64 int intptr_t; +#endif +#define _INTPTR_T_DEFINED +#endif + +#endif /*__MINGW32__*/ diff --git a/Include/pcap.h b/Include/pcap.h new file mode 100644 index 0000000..935f949 --- /dev/null +++ b/Include/pcap.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.59 2006/10/04 18:09:22 guy Exp $ (LBL) + */ + +/* + * For backwards compatibility. + * + * Note to OS vendors: do NOT get rid of this file! Many applications + * expect to be able to include , and at least some of them + * go through contortions in their configure scripts to try to detect + * OSes that have "helpfully" moved pcap.h to without + * leaving behind a file. + */ +#include diff --git a/Include/pcap/bluetooth.h b/Include/pcap/bluetooth.h new file mode 100644 index 0000000..7bf65df --- /dev/null +++ b/Include/pcap/bluetooth.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006 Paolo Abeni (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * bluetooth data struct + * By Paolo Abeni + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/bluetooth.h,v 1.1 2007/09/22 02:10:17 guy Exp $ + */ + +#ifndef _PCAP_BLUETOOTH_STRUCTS_H__ +#define _PCAP_BLUETOOTH_STRUCTS_H__ + +/* + * Header prepended libpcap to each bluetooth h:4 frame. + * fields are in network byte order + */ +typedef struct _pcap_bluetooth_h4_header { + u_int32_t direction; /* if first bit is set direction is incoming */ +} pcap_bluetooth_h4_header; + + +#endif diff --git a/Include/pcap/bpf.h b/Include/pcap/bpf.h new file mode 100644 index 0000000..9f4ca33 --- /dev/null +++ b/Include/pcap/bpf.h @@ -0,0 +1,934 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.h 7.1 (Berkeley) 5/7/91 + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/bpf.h,v 1.19.2.8 2008-09-22 20:16:01 guy Exp $ (LBL) + */ + +/* + * This is libpcap's cut-down version of bpf.h; it includes only + * the stuff needed for the code generator and the userland BPF + * interpreter, and the libpcap APIs for setting filters, etc.. + * + * "pcap-bpf.c" will include the native OS version, as it deals with + * the OS's BPF implementation. + * + * XXX - should this all just be moved to "pcap.h"? + */ + +#ifndef BPF_MAJOR_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/* BSD style release date */ +#define BPF_RELEASE 199606 + +#ifdef MSDOS /* must be 32-bit */ +typedef long bpf_int32; +typedef unsigned long bpf_u_int32; +#else +typedef int bpf_int32; +typedef u_int bpf_u_int32; +#endif + +/* + * Alignment macros. BPF_WORDALIGN rounds up to the next + * even multiple of BPF_ALIGNMENT. + */ +#ifndef __NetBSD__ +#define BPF_ALIGNMENT sizeof(bpf_int32) +#else +#define BPF_ALIGNMENT sizeof(long) +#endif +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_MAXBUFSIZE 0x8000 +#define BPF_MINBUFSIZE 32 + +/* + * Structure for "pcap_compile()", "pcap_setfilter()", etc.. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +/* + * Struct return by BIOCVERSION. This represents the version number of + * the filter language described by the instruction encodings below. + * bpf understands a program iff kernel_major == filter_major && + * kernel_minor >= filter_minor, that is, if the value returned by the + * running kernel has the same major number and a minor number equal + * equal to or less than the filter being downloaded. Otherwise, the + * results are undefined, meaning an error may be returned or packets + * may be accepted haphazardly. + * It has nothing to do with the source code version. + */ +struct bpf_version { + u_short bv_major; + u_short bv_minor; +}; +/* Current version number of filter architecture. */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * Data-link level type codes. + * + * Do *NOT* add new values to this list without asking + * "tcpdump-workers@lists.tcpdump.org" for a value. Otherwise, you run + * the risk of using a value that's already being used for some other + * purpose, and of having tools that read libpcap-format captures not + * being able to handle captures with your new DLT_ value, with no hope + * that they will ever be changed to do so (as that would destroy their + * ability to read captures using that value for that other purpose). + */ + +/* + * These are the types that are the same on all platforms, and that + * have been defined by for ages. + */ +#define DLT_NULL 0 /* BSD loopback encapsulation */ +#define DLT_EN10MB 1 /* Ethernet (10Mb) */ +#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */ +#define DLT_AX25 3 /* Amateur Radio AX.25 */ +#define DLT_PRONET 4 /* Proteon ProNET Token Ring */ +#define DLT_CHAOS 5 /* Chaos */ +#define DLT_IEEE802 6 /* 802.5 Token Ring */ +#define DLT_ARCNET 7 /* ARCNET, with BSD-style header */ +#define DLT_SLIP 8 /* Serial Line IP */ +#define DLT_PPP 9 /* Point-to-point Protocol */ +#define DLT_FDDI 10 /* FDDI */ + +/* + * These are types that are different on some platforms, and that + * have been defined by for ages. We use #ifdefs to + * detect the BSDs that define them differently from the traditional + * libpcap + * + * XXX - DLT_ATM_RFC1483 is 13 in BSD/OS, and DLT_RAW is 14 in BSD/OS, + * but I don't know what the right #define is for BSD/OS. + */ +#define DLT_ATM_RFC1483 11 /* LLC-encapsulated ATM */ + +#ifdef __OpenBSD__ +#define DLT_RAW 14 /* raw IP */ +#else +#define DLT_RAW 12 /* raw IP */ +#endif + +/* + * Given that the only OS that currently generates BSD/OS SLIP or PPP + * is, well, BSD/OS, arguably everybody should have chosen its values + * for DLT_SLIP_BSDOS and DLT_PPP_BSDOS, which are 15 and 16, but they + * didn't. So it goes. + */ +#if defined(__NetBSD__) || defined(__FreeBSD__) +#ifndef DLT_SLIP_BSDOS +#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */ +#endif +#else +#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */ +#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */ +#endif + +/* + * 17 is used for DLT_OLD_PFLOG in OpenBSD; + * OBSOLETE: DLT_PFLOG is 117 in OpenBSD now as well. See below. + * 18 is used for DLT_PFSYNC in OpenBSD; don't use it for anything else. + */ + +#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */ + +/* + * Apparently Redback uses this for its SmartEdge 400/800. I hope + * nobody else decided to use it, too. + */ +#define DLT_REDBACK_SMARTEDGE 32 + +/* + * These values are defined by NetBSD; other platforms should refrain from + * using them for other purposes, so that NetBSD savefiles with link + * types of 50 or 51 can be read as this type on all platforms. + */ +#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */ +#define DLT_PPP_ETHER 51 /* PPP over Ethernet */ + +/* + * The Axent Raptor firewall - now the Symantec Enterprise Firewall - uses + * a link-layer type of 99 for the tcpdump it supplies. The link-layer + * header has 6 bytes of unknown data, something that appears to be an + * Ethernet type, and 36 bytes that appear to be 0 in at least one capture + * I've seen. + */ +#define DLT_SYMANTEC_FIREWALL 99 + +/* + * Values between 100 and 103 are used in capture file headers as + * link-layer types corresponding to DLT_ types that differ + * between platforms; don't use those values for new DLT_ new types. + */ + +/* + * This value was defined by libpcap 0.5; platforms that have defined + * it with a different value should define it here with that value - + * a link type of 104 in a save file will be mapped to DLT_C_HDLC, + * whatever value that happens to be, so programs will correctly + * handle files with that link type regardless of the value of + * DLT_C_HDLC. + * + * The name DLT_C_HDLC was used by BSD/OS; we use that name for source + * compatibility with programs written for BSD/OS. + * + * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well, + * for source compatibility with programs written for libpcap 0.5. + */ +#define DLT_C_HDLC 104 /* Cisco HDLC */ +#define DLT_CHDLC DLT_C_HDLC + +#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */ + +/* + * 106 is reserved for Linux Classical IP over ATM; it's like DLT_RAW, + * except when it isn't. (I.e., sometimes it's just raw IP, and + * sometimes it isn't.) We currently handle it as DLT_LINUX_SLL, + * so that we don't have to worry about the link-layer header.) + */ + +/* + * Frame Relay; BSD/OS has a DLT_FR with a value of 11, but that collides + * with other values. + * DLT_FR and DLT_FRELAY packets start with the Q.922 Frame Relay header + * (DLCI, etc.). + */ +#define DLT_FRELAY 107 + +/* + * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except + * that the AF_ type in the link-layer header is in network byte order. + * + * DLT_LOOP is 12 in OpenBSD, but that's DLT_RAW in other OSes, so + * we don't use 12 for it in OSes other than OpenBSD. + */ +#ifdef __OpenBSD__ +#define DLT_LOOP 12 +#else +#define DLT_LOOP 108 +#endif + +/* + * Encapsulated packets for IPsec; DLT_ENC is 13 in OpenBSD, but that's + * DLT_SLIP_BSDOS in NetBSD, so we don't use 13 for it in OSes other + * than OpenBSD. + */ +#ifdef __OpenBSD__ +#define DLT_ENC 13 +#else +#define DLT_ENC 109 +#endif + +/* + * Values between 110 and 112 are reserved for use in capture file headers + * as link-layer types corresponding to DLT_ types that might differ + * between platforms; don't use those values for new DLT_ types + * other than the corresponding DLT_ types. + */ + +/* + * This is for Linux cooked sockets. + */ +#define DLT_LINUX_SLL 113 + +/* + * Apple LocalTalk hardware. + */ +#define DLT_LTALK 114 + +/* + * Acorn Econet. + */ +#define DLT_ECONET 115 + +/* + * Reserved for use with OpenBSD ipfilter. + */ +#define DLT_IPFILTER 116 + +/* + * OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD, but that's DLT_LANE8023 + * in SuSE 6.3, so we can't use 17 for it in capture-file headers. + * + * XXX: is there a conflict with DLT_PFSYNC 18 as well? + */ +#ifdef __OpenBSD__ +#define DLT_OLD_PFLOG 17 +#define DLT_PFSYNC 18 +#endif +#define DLT_PFLOG 117 + +/* + * Registered for Cisco-internal use. + */ +#define DLT_CISCO_IOS 118 + +/* + * For 802.11 cards using the Prism II chips, with a link-layer + * header including Prism monitor mode information plus an 802.11 + * header. + */ +#define DLT_PRISM_HEADER 119 + +/* + * Reserved for Aironet 802.11 cards, with an Aironet link-layer header + * (see Doug Ambrisko's FreeBSD patches). + */ +#define DLT_AIRONET_HEADER 120 + +/* + * Reserved for Siemens HiPath HDLC. + */ +#define DLT_HHDLC 121 + +/* + * This is for RFC 2625 IP-over-Fibre Channel. + * + * This is not for use with raw Fibre Channel, where the link-layer + * header starts with a Fibre Channel frame header; it's for IP-over-FC, + * where the link-layer header starts with an RFC 2625 Network_Header + * field. + */ +#define DLT_IP_OVER_FC 122 + +/* + * This is for Full Frontal ATM on Solaris with SunATM, with a + * pseudo-header followed by an AALn PDU. + * + * There may be other forms of Full Frontal ATM on other OSes, + * with different pseudo-headers. + * + * If ATM software returns a pseudo-header with VPI/VCI information + * (and, ideally, packet type information, e.g. signalling, ILMI, + * LANE, LLC-multiplexed traffic, etc.), it should not use + * DLT_ATM_RFC1483, but should get a new DLT_ value, so tcpdump + * and the like don't have to infer the presence or absence of a + * pseudo-header and the form of the pseudo-header. + */ +#define DLT_SUNATM 123 /* Solaris+SunATM */ + +/* + * Reserved as per request from Kent Dahlgren + * for private use. + */ +#define DLT_RIO 124 /* RapidIO */ +#define DLT_PCI_EXP 125 /* PCI Express */ +#define DLT_AURORA 126 /* Xilinx Aurora link layer */ + +/* + * Header for 802.11 plus a number of bits of link-layer information + * including radio information, used by some recent BSD drivers as + * well as the madwifi Atheros driver for Linux. + */ +#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus radiotap radio header */ + +/* + * Reserved for the TZSP encapsulation, as per request from + * Chris Waters + * TZSP is a generic encapsulation for any other link type, + * which includes a means to include meta-information + * with the packet, e.g. signal strength and channel + * for 802.11 packets. + */ +#define DLT_TZSP 128 /* Tazmen Sniffer Protocol */ + +/* + * BSD's ARCNET headers have the source host, destination host, + * and type at the beginning of the packet; that's what's handed + * up to userland via BPF. + * + * Linux's ARCNET headers, however, have a 2-byte offset field + * between the host IDs and the type; that's what's handed up + * to userland via PF_PACKET sockets. + * + * We therefore have to have separate DLT_ values for them. + */ +#define DLT_ARCNET_LINUX 129 /* ARCNET */ + +/* + * Juniper-private data link types, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, etc.. + */ +#define DLT_JUNIPER_MLPPP 130 +#define DLT_JUNIPER_MLFR 131 +#define DLT_JUNIPER_ES 132 +#define DLT_JUNIPER_GGSN 133 +#define DLT_JUNIPER_MFR 134 +#define DLT_JUNIPER_ATM2 135 +#define DLT_JUNIPER_SERVICES 136 +#define DLT_JUNIPER_ATM1 137 + +/* + * Apple IP-over-IEEE 1394, as per a request from Dieter Siegmund + * . The header that's presented is an Ethernet-like + * header: + * + * #define FIREWIRE_EUI64_LEN 8 + * struct firewire_header { + * u_char firewire_dhost[FIREWIRE_EUI64_LEN]; + * u_char firewire_shost[FIREWIRE_EUI64_LEN]; + * u_short firewire_type; + * }; + * + * with "firewire_type" being an Ethernet type value, rather than, + * for example, raw GASP frames being handed up. + */ +#define DLT_APPLE_IP_OVER_IEEE1394 138 + +/* + * Various SS7 encapsulations, as per a request from Jeff Morriss + * and subsequent discussions. + */ +#define DLT_MTP2_WITH_PHDR 139 /* pseudo-header with various info, followed by MTP2 */ +#define DLT_MTP2 140 /* MTP2, without pseudo-header */ +#define DLT_MTP3 141 /* MTP3, without pseudo-header or MTP2 */ +#define DLT_SCCP 142 /* SCCP, without pseudo-header or MTP2 or MTP3 */ + +/* + * DOCSIS MAC frames. + */ +#define DLT_DOCSIS 143 + +/* + * Linux-IrDA packets. Protocol defined at http://www.irda.org. + * Those packets include IrLAP headers and above (IrLMP...), but + * don't include Phy framing (SOF/EOF/CRC & byte stuffing), because Phy + * framing can be handled by the hardware and depend on the bitrate. + * This is exactly the format you would get capturing on a Linux-IrDA + * interface (irdaX), but not on a raw serial port. + * Note the capture is done in "Linux-cooked" mode, so each packet include + * a fake packet header (struct sll_header). This is because IrDA packet + * decoding is dependant on the direction of the packet (incomming or + * outgoing). + * When/if other platform implement IrDA capture, we may revisit the + * issue and define a real DLT_IRDA... + * Jean II + */ +#define DLT_LINUX_IRDA 144 + +/* + * Reserved for IBM SP switch and IBM Next Federation switch. + */ +#define DLT_IBM_SP 145 +#define DLT_IBM_SN 146 + +/* + * Reserved for private use. If you have some link-layer header type + * that you want to use within your organization, with the capture files + * using that link-layer header type not ever be sent outside your + * organization, you can use these values. + * + * No libpcap release will use these for any purpose, nor will any + * tcpdump release use them, either. + * + * Do *NOT* use these in capture files that you expect anybody not using + * your private versions of capture-file-reading tools to read; in + * particular, do *NOT* use them in products, otherwise you may find that + * people won't be able to use tcpdump, or snort, or Ethereal, or... to + * read capture files from your firewall/intrusion detection/traffic + * monitoring/etc. appliance, or whatever product uses that DLT_ value, + * and you may also find that the developers of those applications will + * not accept patches to let them read those files. + * + * Also, do not use them if somebody might send you a capture using them + * for *their* private type and tools using them for *your* private type + * would have to read them. + * + * Instead, ask "tcpdump-workers@lists.tcpdump.org" for a new DLT_ value, + * as per the comment above, and use the type you're given. + */ +#define DLT_USER0 147 +#define DLT_USER1 148 +#define DLT_USER2 149 +#define DLT_USER3 150 +#define DLT_USER4 151 +#define DLT_USER5 152 +#define DLT_USER6 153 +#define DLT_USER7 154 +#define DLT_USER8 155 +#define DLT_USER9 156 +#define DLT_USER10 157 +#define DLT_USER11 158 +#define DLT_USER12 159 +#define DLT_USER13 160 +#define DLT_USER14 161 +#define DLT_USER15 162 + +/* + * For future use with 802.11 captures - defined by AbsoluteValue + * Systems to store a number of bits of link-layer information + * including radio information: + * + * http://www.shaftnet.org/~pizza/software/capturefrm.txt + * + * but it might be used by some non-AVS drivers now or in the + * future. + */ +#define DLT_IEEE802_11_RADIO_AVS 163 /* 802.11 plus AVS radio header */ + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, etc.. + */ +#define DLT_JUNIPER_MONITOR 164 + +/* + * Reserved for BACnet MS/TP. + */ +#define DLT_BACNET_MS_TP 165 + +/* + * Another PPP variant as per request from Karsten Keil . + * + * This is used in some OSes to allow a kernel socket filter to distinguish + * between incoming and outgoing packets, on a socket intended to + * supply pppd with outgoing packets so it can do dial-on-demand and + * hangup-on-lack-of-demand; incoming packets are filtered out so they + * don't cause pppd to hold the connection up (you don't want random + * input packets such as port scans, packets from old lost connections, + * etc. to force the connection to stay up). + * + * The first byte of the PPP header (0xff03) is modified to accomodate + * the direction - 0x00 = IN, 0x01 = OUT. + */ +#define DLT_PPP_PPPD 166 + +/* + * Names for backwards compatibility with older versions of some PPP + * software; new software should use DLT_PPP_PPPD. + */ +#define DLT_PPP_WITH_DIRECTION DLT_PPP_PPPD +#define DLT_LINUX_PPP_WITHDIRECTION DLT_PPP_PPPD + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_s are used + * for passing on chassis-internal metainformation such as + * QOS profiles, cookies, etc.. + */ +#define DLT_JUNIPER_PPPOE 167 +#define DLT_JUNIPER_PPPOE_ATM 168 + +#define DLT_GPRS_LLC 169 /* GPRS LLC */ +#define DLT_GPF_T 170 /* GPF-T (ITU-T G.7041/Y.1303) */ +#define DLT_GPF_F 171 /* GPF-F (ITU-T G.7041/Y.1303) */ + +/* + * Requested by Oolan Zimmer for use in Gcom's T1/E1 line + * monitoring equipment. + */ +#define DLT_GCOM_T1E1 172 +#define DLT_GCOM_SERIAL 173 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . The DLT_ is used + * for internal communication to Physical Interface Cards (PIC) + */ +#define DLT_JUNIPER_PIC_PEER 174 + +/* + * Link types requested by Gregor Maier of Endace + * Measurement Systems. They add an ERF header (see + * http://www.endace.com/support/EndaceRecordFormat.pdf) in front of + * the link-layer header. + */ +#define DLT_ERF_ETH 175 /* Ethernet */ +#define DLT_ERF_POS 176 /* Packet-over-SONET */ + +/* + * Requested by Daniele Orlandi for raw LAPD + * for vISDN (http://www.orlandi.com/visdn/). Its link-layer header + * includes additional information before the LAPD header, so it's + * not necessarily a generic LAPD header. + */ +#define DLT_LINUX_LAPD 177 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ are used for prepending meta-information + * like interface index, interface name + * before standard Ethernet, PPP, Frelay & C-HDLC Frames + */ +#define DLT_JUNIPER_ETHER 178 +#define DLT_JUNIPER_PPP 179 +#define DLT_JUNIPER_FRELAY 180 +#define DLT_JUNIPER_CHDLC 181 + +/* + * Multi Link Frame Relay (FRF.16) + */ +#define DLT_MFR 182 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for internal communication with a + * voice Adapter Card (PIC) + */ +#define DLT_JUNIPER_VP 183 + +/* + * Arinc 429 frames. + * DLT_ requested by Gianluca Varenni . + * Every frame contains a 32bit A429 label. + * More documentation on Arinc 429 can be found at + * http://www.condoreng.com/support/downloads/tutorials/ARINCTutorial.pdf + */ +#define DLT_A429 184 + +/* + * Arinc 653 Interpartition Communication messages. + * DLT_ requested by Gianluca Varenni . + * Please refer to the A653-1 standard for more information. + */ +#define DLT_A653_ICM 185 + +/* + * USB packets, beginning with a USB setup header; requested by + * Paolo Abeni . + */ +#define DLT_USB 186 + +/* + * Bluetooth HCI UART transport layer (part H:4); requested by + * Paolo Abeni. + */ +#define DLT_BLUETOOTH_HCI_H4 187 + +/* + * IEEE 802.16 MAC Common Part Sublayer; requested by Maria Cruz + * . + */ +#define DLT_IEEE802_16_MAC_CPS 188 + +/* + * USB packets, beginning with a Linux USB header; requested by + * Paolo Abeni . + */ +#define DLT_USB_LINUX 189 + +/* + * Controller Area Network (CAN) v. 2.0B packets. + * DLT_ requested by Gianluca Varenni . + * Used to dump CAN packets coming from a CAN Vector board. + * More documentation on the CAN v2.0B frames can be found at + * http://www.can-cia.org/downloads/?269 + */ +#define DLT_CAN20B 190 + +/* + * IEEE 802.15.4, with address fields padded, as is done by Linux + * drivers; requested by Juergen Schimmer. + */ +#define DLT_IEEE802_15_4_LINUX 191 + +/* + * Per Packet Information encapsulated packets. + * DLT_ requested by Gianluca Varenni . + */ +#define DLT_PPI 192 + +/* + * Header for 802.16 MAC Common Part Sublayer plus a radiotap radio header; + * requested by Charles Clancy. + */ +#define DLT_IEEE802_16_MAC_CPS_RADIO 193 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for internal communication with a + * integrated service module (ISM). + */ +#define DLT_JUNIPER_ISM 194 + +/* + * IEEE 802.15.4, exactly as it appears in the spec (no padding, no + * nothing); requested by Mikko Saarnivala . + */ +#define DLT_IEEE802_15_4 195 + +/* + * Various link-layer types, with a pseudo-header, for SITA + * (http://www.sita.aero/); requested by Fulko Hew (fulko.hew@gmail.com). + */ +#define DLT_SITA 196 + +/* + * Various link-layer types, with a pseudo-header, for Endace DAG cards; + * encapsulates Endace ERF records. Requested by Stephen Donnelly + * . + */ +#define DLT_ERF 197 + +/* + * Special header prepended to Ethernet packets when capturing from a + * u10 Networks board. Requested by Phil Mulholland + * . + */ +#define DLT_RAIF1 198 + +/* + * IPMB packet for IPMI, beginning with the I2C slave address, followed + * by the netFn and LUN, etc.. Requested by Chanthy Toeung + * . + */ +#define DLT_IPMB 199 + +/* + * Juniper-private data link type, as per request from + * Hannes Gredler . + * The DLT_ is used for capturing data on a secure tunnel interface. + */ +#define DLT_JUNIPER_ST 200 + +/* + * Bluetooth HCI UART transport layer (part H:4), with pseudo-header + * that includes direction information; requested by Paolo Abeni. + */ +#define DLT_BLUETOOTH_HCI_H4_WITH_PHDR 201 + +/* + * AX.25 packet with a 1-byte KISS header; see + * + * http://www.ax25.net/kiss.htm + * + * as per Richard Stearn . + */ +#define DLT_AX25_KISS 202 + +/* + * LAPD packets from an ISDN channel, starting with the address field, + * with no pseudo-header. + * Requested by Varuna De Silva . + */ +#define DLT_LAPD 203 + +/* + * Variants of various link-layer headers, with a one-byte direction + * pseudo-header prepended - zero means "received by this host", + * non-zero (any non-zero value) means "sent by this host" - as per + * Will Barker . + */ +#define DLT_PPP_WITH_DIR 204 /* PPP - don't confuse with DLT_PPP_WITH_DIRECTION */ +#define DLT_C_HDLC_WITH_DIR 205 /* Cisco HDLC */ +#define DLT_FRELAY_WITH_DIR 206 /* Frame Relay */ +#define DLT_LAPB_WITH_DIR 207 /* LAPB */ + +/* + * 208 is reserved for an as-yet-unspecified proprietary link-layer + * type, as requested by Will Barker. + */ + +/* + * IPMB with a Linux-specific pseudo-header; as requested by Alexey Neyman + * . + */ +#define DLT_IPMB_LINUX 209 + +/* + * FlexRay automotive bus - http://www.flexray.com/ - as requested + * by Hannes Kaelber . + */ +#define DLT_FLEXRAY 210 + +/* + * Media Oriented Systems Transport (MOST) bus for multimedia + * transport - http://www.mostcooperation.com/ - as requested + * by Hannes Kaelber . + */ +#define DLT_MOST 211 + +/* + * Local Interconnect Network (LIN) bus for vehicle networks - + * http://www.lin-subbus.org/ - as requested by Hannes Kaelber + * . + */ +#define DLT_LIN 212 + +/* + * X2E-private data link type used for serial line capture, + * as requested by Hannes Kaelber . + */ +#define DLT_X2E_SERIAL 213 + +/* + * X2E-private data link type used for the Xoraya data logger + * family, as requested by Hannes Kaelber . + */ +#define DLT_X2E_XORAYA 214 + +/* + * IEEE 802.15.4, exactly as it appears in the spec (no padding, no + * nothing), but with the PHY-level data for non-ASK PHYs (4 octets + * of 0 as preamble, one octet of SFD, one octet of frame length+ + * reserved bit, and then the MAC-layer data, starting with the + * frame control field). + * + * Requested by Max Filippov . + */ +#define DLT_IEEE802_15_4_NONASK_PHY 215 + + +/* + * DLT and savefile link type values are split into a class and + * a member of that class. A class value of 0 indicates a regular + * DLT_/LINKTYPE_ value. + */ +#define DLT_CLASS(x) ((x) & 0x03ff0000) + +/* + * NetBSD-specific generic "raw" link type. The class value indicates + * that this is the generic raw type, and the lower 16 bits are the + * address family we're dealing with. Those values are NetBSD-specific; + * do not assume that they correspond to AF_ values for your operating + * system. + */ +#define DLT_CLASS_NETBSD_RAWAF 0x02240000 +#define DLT_NETBSD_RAWAF(af) (DLT_CLASS_NETBSD_RAWAF | (af)) +#define DLT_NETBSD_RAWAF_AF(x) ((x) & 0x0000ffff) +#define DLT_IS_NETBSD_RAWAF(x) (DLT_CLASS(x) == DLT_CLASS_NETBSD_RAWAF) + + +/* + * The instruction encodings. + */ +/* instruction classes */ +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct bpf_insn { + u_short code; + u_char jt; + u_char jf; + bpf_u_int32 k; +}; + +/* + * Macros for insn array initializers. + */ +#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k } + +#if __STDC__ || defined(__cplusplus) +extern int bpf_validate(const struct bpf_insn *, int); +extern u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +#else +extern int bpf_validate(); +extern u_int bpf_filter(); +#endif + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Include/pcap/namedb.h b/Include/pcap/namedb.h new file mode 100644 index 0000000..9002c75 --- /dev/null +++ b/Include/pcap/namedb.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1994, 1996 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/namedb.h,v 1.1 2006/10/04 18:09:22 guy Exp $ (LBL) + */ + +#ifndef lib_pcap_namedb_h +#define lib_pcap_namedb_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * As returned by the pcap_next_etherent() + * XXX this stuff doesn't belong in this interface, but this + * library already must do name to address translation, so + * on systems that don't have support for /etc/ethers, we + * export these hooks since they'll + */ +struct pcap_etherent { + u_char addr[6]; + char name[122]; +}; +#ifndef PCAP_ETHERS_FILE +#define PCAP_ETHERS_FILE "/etc/ethers" +#endif +struct pcap_etherent *pcap_next_etherent(FILE *); +u_char *pcap_ether_hostton(const char*); +u_char *pcap_ether_aton(const char *); + +bpf_u_int32 **pcap_nametoaddr(const char *); +#ifdef INET6 +struct addrinfo *pcap_nametoaddrinfo(const char *); +#endif +bpf_u_int32 pcap_nametonetaddr(const char *); + +int pcap_nametoport(const char *, int *, int *); +int pcap_nametoportrange(const char *, int *, int *, int *); +int pcap_nametoproto(const char *); +int pcap_nametoeproto(const char *); +int pcap_nametollc(const char *); +/* + * If a protocol is unknown, PROTO_UNDEF is returned. + * Also, pcap_nametoport() returns the protocol along with the port number. + * If there are ambiguous entried in /etc/services (i.e. domain + * can be either tcp or udp) PROTO_UNDEF is returned. + */ +#define PROTO_UNDEF -1 + +/* XXX move these to pcap-int.h? */ +int __pcap_atodn(const char *, bpf_u_int32 *); +int __pcap_atoin(const char *, bpf_u_int32 *); +u_short __pcap_nametodnaddr(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Include/pcap/pcap.h b/Include/pcap/pcap.h new file mode 100644 index 0000000..ad8fc40 --- /dev/null +++ b/Include/pcap/pcap.h @@ -0,0 +1,407 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */ +/* + * Copyright (c) 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the Computer Systems + * Engineering Group at Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/pcap.h,v 1.4.2.11 2008-10-06 15:38:39 gianluca Exp $ (LBL) + */ + +#ifndef lib_pcap_pcap_h +#define lib_pcap_pcap_h + +#if defined(WIN32) + #include +#elif defined(MSDOS) + #include + #include /* u_int, u_char etc. */ +#else /* UN*X */ + #include + #include +#endif /* WIN32/MSDOS/UN*X */ + +#ifndef PCAP_DONT_INCLUDE_PCAP_BPF_H +#include +#endif + +#include + +#ifdef HAVE_REMOTE + // We have to define the SOCKET here, although it has been defined in sockutils.h + // This is to avoid the distribution of the 'sockutils.h' file around + // (for example in the WinPcap developer's pack) + #ifndef SOCKET + #ifdef WIN32 + #define SOCKET unsigned int + #else + #define SOCKET int + #endif + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define PCAP_ERRBUF_SIZE 256 + +/* + * Compatibility for systems that have a bpf.h that + * predates the bpf typedefs for 64-bit support. + */ +#if BPF_RELEASE - 0 < 199406 +typedef int bpf_int32; +typedef u_int bpf_u_int32; +#endif + +typedef struct pcap pcap_t; +typedef struct pcap_dumper pcap_dumper_t; +typedef struct pcap_if pcap_if_t; +typedef struct pcap_addr pcap_addr_t; + +/* + * The first record in the file contains saved values for some + * of the flags used in the printout phases of tcpdump. + * Many fields here are 32 bit ints so compilers won't insert unwanted + * padding; these files need to be interchangeable across architectures. + * + * Do not change the layout of this structure, in any way (this includes + * changes that only affect the length of fields in this structure). + * + * Also, do not change the interpretation of any of the members of this + * structure, in any way (this includes using values other than + * LINKTYPE_ values, as defined in "savefile.c", in the "linktype" + * field). + * + * Instead: + * + * introduce a new structure for the new format, if the layout + * of the structure changed; + * + * send mail to "tcpdump-workers@lists.tcpdump.org", requesting + * a new magic number for your new capture file format, and, when + * you get the new magic number, put it in "savefile.c"; + * + * use that magic number for save files with the changed file + * header; + * + * make the code in "savefile.c" capable of reading files with + * the old file header as well as files with the new file header + * (using the magic number to determine the header format). + * + * Then supply the changes as a patch at + * + * http://sourceforge.net/projects/libpcap/ + * + * so that future versions of libpcap and programs that use it (such as + * tcpdump) will be able to read your new capture file format. + */ +struct pcap_file_header { + bpf_u_int32 magic; + u_short version_major; + u_short version_minor; + bpf_int32 thiszone; /* gmt to local correction */ + bpf_u_int32 sigfigs; /* accuracy of timestamps */ + bpf_u_int32 snaplen; /* max length saved portion of each pkt */ + bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */ +}; + +/* + * Macros for the value returned by pcap_datalink_ext(). + * + * If LT_FCS_LENGTH_PRESENT(x) is true, the LT_FCS_LENGTH(x) macro + * gives the FCS length of packets in the capture. + */ +#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000) +#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28) +#define LT_FCS_DATALINK_EXT(x) ((((x) & 0xF) << 28) | 0x04000000) + +typedef enum { + PCAP_D_INOUT = 0, + PCAP_D_IN, + PCAP_D_OUT +} pcap_direction_t; + +/* + * Generic per-packet information, as supplied by libpcap. + * + * The time stamp can and should be a "struct timeval", regardless of + * whether your system supports 32-bit tv_sec in "struct timeval", + * 64-bit tv_sec in "struct timeval", or both if it supports both 32-bit + * and 64-bit applications. The on-disk format of savefiles uses 32-bit + * tv_sec (and tv_usec); this structure is irrelevant to that. 32-bit + * and 64-bit versions of libpcap, even if they're on the same platform, + * should supply the appropriate version of "struct timeval", even if + * that's not what the underlying packet capture mechanism supplies. + */ +struct pcap_pkthdr { + struct timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +}; + +/* + * As returned by the pcap_stats() + */ +struct pcap_stat { + u_int ps_recv; /* number of packets received */ + u_int ps_drop; /* number of packets dropped */ + u_int ps_ifdrop; /* drops by interface XXX not yet supported */ +#ifdef HAVE_REMOTE + u_int ps_capt; /* number of packets that are received by the application; please get rid off the Win32 ifdef */ + u_int ps_sent; /* number of packets sent by the server on the network */ + u_int ps_netdrop; /* number of packets lost on the network */ +#endif /* HAVE_REMOTE */ +}; + +#ifdef MSDOS +/* + * As returned by the pcap_stats_ex() + */ +struct pcap_stat_ex { + u_long rx_packets; /* total packets received */ + u_long tx_packets; /* total packets transmitted */ + u_long rx_bytes; /* total bytes received */ + u_long tx_bytes; /* total bytes transmitted */ + u_long rx_errors; /* bad packets received */ + u_long tx_errors; /* packet transmit problems */ + u_long rx_dropped; /* no space in Rx buffers */ + u_long tx_dropped; /* no space available for Tx */ + u_long multicast; /* multicast packets received */ + u_long collisions; + + /* detailed rx_errors: */ + u_long rx_length_errors; + u_long rx_over_errors; /* receiver ring buff overflow */ + u_long rx_crc_errors; /* recv'd pkt with crc error */ + u_long rx_frame_errors; /* recv'd frame alignment error */ + u_long rx_fifo_errors; /* recv'r fifo overrun */ + u_long rx_missed_errors; /* recv'r missed packet */ + + /* detailed tx_errors */ + u_long tx_aborted_errors; + u_long tx_carrier_errors; + u_long tx_fifo_errors; + u_long tx_heartbeat_errors; + u_long tx_window_errors; + }; +#endif + +/* + * Item in a list of interfaces. + */ +struct pcap_if { + struct pcap_if *next; + char *name; /* name to hand to "pcap_open_live()" */ + char *description; /* textual description of interface, or NULL */ + struct pcap_addr *addresses; + bpf_u_int32 flags; /* PCAP_IF_ interface flags */ +}; + +#define PCAP_IF_LOOPBACK 0x00000001 /* interface is loopback */ + +/* + * Representation of an interface address. + */ +struct pcap_addr { + struct pcap_addr *next; + struct sockaddr *addr; /* address */ + struct sockaddr *netmask; /* netmask for that address */ + struct sockaddr *broadaddr; /* broadcast address for that address */ + struct sockaddr *dstaddr; /* P2P destination address for that address */ +}; + +typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, + const u_char *); + +/* + * Error codes for the pcap API. + * These will all be negative, so you can check for the success or + * failure of a call that returns these codes by checking for a + * negative value. + */ +#define PCAP_ERROR -1 /* generic error code */ +#define PCAP_ERROR_BREAK -2 /* loop terminated by pcap_breakloop */ +#define PCAP_ERROR_NOT_ACTIVATED -3 /* the capture needs to be activated */ +#define PCAP_ERROR_ACTIVATED -4 /* the operation can't be performed on already activated captures */ +#define PCAP_ERROR_NO_SUCH_DEVICE -5 /* no such device exists */ +#define PCAP_ERROR_RFMON_NOTSUP -6 /* this device doesn't support rfmon (monitor) mode */ +#define PCAP_ERROR_NOT_RFMON -7 /* operation supported only in monitor mode */ +#define PCAP_ERROR_PERM_DENIED -8 /* no permission to open the device */ +#define PCAP_ERROR_IFACE_NOT_UP -9 /* interface isn't up */ + +/* + * Warning codes for the pcap API. + * These will all be positive and non-zero, so they won't look like + * errors. + */ +#define PCAP_WARNING 1 /* generic warning code */ +#define PCAP_WARNING_PROMISC_NOTSUP 2 /* this device doesn't support promiscuous mode */ + +char *pcap_lookupdev(char *); +int pcap_lookupnet(const char *, bpf_u_int32 *, bpf_u_int32 *, char *); + +pcap_t *pcap_create(const char *, char *); +int pcap_set_snaplen(pcap_t *, int); +int pcap_set_promisc(pcap_t *, int); +int pcap_can_set_rfmon(pcap_t *); +int pcap_set_rfmon(pcap_t *, int); +int pcap_set_timeout(pcap_t *, int); +int pcap_set_buffer_size(pcap_t *, int); +int pcap_activate(pcap_t *); + +pcap_t *pcap_open_live(const char *, int, int, int, char *); +pcap_t *pcap_open_dead(int, int); +pcap_t *pcap_open_offline(const char *, char *); +#if defined(WIN32) +pcap_t *pcap_hopen_offline(intptr_t, char *); +#if !defined(LIBPCAP_EXPORTS) +#define pcap_fopen_offline(f,b) \ + pcap_hopen_offline(_get_osfhandle(_fileno(f)), b) +#else /*LIBPCAP_EXPORTS*/ +static pcap_t *pcap_fopen_offline(FILE *, char *); +#endif +#else /*WIN32*/ +pcap_t *pcap_fopen_offline(FILE *, char *); +#endif /*WIN32*/ + +void pcap_close(pcap_t *); +int pcap_loop(pcap_t *, int, pcap_handler, u_char *); +int pcap_dispatch(pcap_t *, int, pcap_handler, u_char *); +const u_char* + pcap_next(pcap_t *, struct pcap_pkthdr *); +int pcap_next_ex(pcap_t *, struct pcap_pkthdr **, const u_char **); +void pcap_breakloop(pcap_t *); +int pcap_stats(pcap_t *, struct pcap_stat *); +int pcap_setfilter(pcap_t *, struct bpf_program *); +int pcap_setdirection(pcap_t *, pcap_direction_t); +int pcap_getnonblock(pcap_t *, char *); +int pcap_setnonblock(pcap_t *, int, char *); +int pcap_inject(pcap_t *, const void *, size_t); +int pcap_sendpacket(pcap_t *, const u_char *, int); +const char *pcap_statustostr(int); +const char *pcap_strerror(int); +char *pcap_geterr(pcap_t *); +void pcap_perror(pcap_t *, char *); +int pcap_compile(pcap_t *, struct bpf_program *, const char *, int, + bpf_u_int32); +int pcap_compile_nopcap(int, int, struct bpf_program *, + const char *, int, bpf_u_int32); +void pcap_freecode(struct bpf_program *); +int pcap_offline_filter(struct bpf_program *, const struct pcap_pkthdr *, + const u_char *); +int pcap_datalink(pcap_t *); +int pcap_datalink_ext(pcap_t *); +int pcap_list_datalinks(pcap_t *, int **); +int pcap_set_datalink(pcap_t *, int); +void pcap_free_datalinks(int *); +int pcap_datalink_name_to_val(const char *); +const char *pcap_datalink_val_to_name(int); +const char *pcap_datalink_val_to_description(int); +int pcap_snapshot(pcap_t *); +int pcap_is_swapped(pcap_t *); +int pcap_major_version(pcap_t *); +int pcap_minor_version(pcap_t *); + +/* XXX */ +FILE *pcap_file(pcap_t *); +int pcap_fileno(pcap_t *); + +pcap_dumper_t *pcap_dump_open(pcap_t *, const char *); +pcap_dumper_t *pcap_dump_fopen(pcap_t *, FILE *fp); +FILE *pcap_dump_file(pcap_dumper_t *); +long pcap_dump_ftell(pcap_dumper_t *); +int pcap_dump_flush(pcap_dumper_t *); +void pcap_dump_close(pcap_dumper_t *); +void pcap_dump(u_char *, const struct pcap_pkthdr *, const u_char *); + +int pcap_findalldevs(pcap_if_t **, char *); +void pcap_freealldevs(pcap_if_t *); + +const char *pcap_lib_version(void); + +/* XXX this guy lives in the bpf tree */ +u_int bpf_filter(const struct bpf_insn *, const u_char *, u_int, u_int); +int bpf_validate(const struct bpf_insn *f, int len); +char *bpf_image(const struct bpf_insn *, int); +void bpf_dump(const struct bpf_program *, int); + +#if defined(WIN32) + +/* + * Win32 definitions + */ + +int pcap_setbuff(pcap_t *p, int dim); +int pcap_setmode(pcap_t *p, int mode); +int pcap_setmintocopy(pcap_t *p, int size); + +#ifdef WPCAP +/* Include file with the wpcap-specific extensions */ +#include +#endif /* WPCAP */ + +#define MODE_CAPT 0 +#define MODE_STAT 1 +#define MODE_MON 2 + +#elif defined(MSDOS) + +/* + * MS-DOS definitions + */ + +int pcap_stats_ex (pcap_t *, struct pcap_stat_ex *); +void pcap_set_wait (pcap_t *p, void (*yield)(void), int wait); +u_long pcap_mac_packets (void); + +#else /* UN*X */ + +/* + * UN*X definitions + */ + +int pcap_get_selectable_fd(pcap_t *); + +#endif /* WIN32/MSDOS/UN*X */ + +#ifdef HAVE_REMOTE +/* Includes most of the public stuff that is needed for the remote capture */ +#include +#endif /* HAVE_REMOTE */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Include/pcap/sll.h b/Include/pcap/sll.h new file mode 100644 index 0000000..e9d5452 --- /dev/null +++ b/Include/pcap/sll.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/sll.h,v 1.2.2.1 2008-05-30 01:36:06 guy Exp $ (LBL) + */ + +/* + * For captures on Linux cooked sockets, we construct a fake header + * that includes: + * + * a 2-byte "packet type" which is one of: + * + * LINUX_SLL_HOST packet was sent to us + * LINUX_SLL_BROADCAST packet was broadcast + * LINUX_SLL_MULTICAST packet was multicast + * LINUX_SLL_OTHERHOST packet was sent to somebody else + * LINUX_SLL_OUTGOING packet was sent *by* us; + * + * a 2-byte Ethernet protocol field; + * + * a 2-byte link-layer type; + * + * a 2-byte link-layer address length; + * + * an 8-byte source link-layer address, whose actual length is + * specified by the previous value. + * + * All fields except for the link-layer address are in network byte order. + * + * DO NOT change the layout of this structure, or change any of the + * LINUX_SLL_ values below. If you must change the link-layer header + * for a "cooked" Linux capture, introduce a new DLT_ type (ask + * "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it + * a value that collides with a value already being used), and use the + * new header in captures of that type, so that programs that can + * handle DLT_LINUX_SLL captures will continue to handle them correctly + * without any change, and so that capture files with different headers + * can be told apart and programs that read them can dissect the + * packets in them. + */ + +#ifndef lib_pcap_sll_h +#define lib_pcap_sll_h + +/* + * A DLT_LINUX_SLL fake link-layer header. + */ +#define SLL_HDR_LEN 16 /* total header length */ +#define SLL_ADDRLEN 8 /* length of address field */ + +struct sll_header { + u_int16_t sll_pkttype; /* packet type */ + u_int16_t sll_hatype; /* link-layer address type */ + u_int16_t sll_halen; /* link-layer address length */ + u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ + u_int16_t sll_protocol; /* protocol */ +}; + +/* + * The LINUX_SLL_ values for "sll_pkttype"; these correspond to the + * PACKET_ values on Linux, but are defined here so that they're + * available even on systems other than Linux, and so that they + * don't change even if the PACKET_ values change. + */ +#define LINUX_SLL_HOST 0 +#define LINUX_SLL_BROADCAST 1 +#define LINUX_SLL_MULTICAST 2 +#define LINUX_SLL_OTHERHOST 3 +#define LINUX_SLL_OUTGOING 4 + +/* + * The LINUX_SLL_ values for "sll_protocol"; these correspond to the + * ETH_P_ values on Linux, but are defined here so that they're + * available even on systems other than Linux. We assume, for now, + * that the ETH_P_ values won't change in Linux; if they do, then: + * + * if we don't translate them in "pcap-linux.c", capture files + * won't necessarily be readable if captured on a system that + * defines ETH_P_ values that don't match these values; + * + * if we do translate them in "pcap-linux.c", that makes life + * unpleasant for the BPF code generator, as the values you test + * for in the kernel aren't the values that you test for when + * reading a capture file, so the fixup code run on BPF programs + * handed to the kernel ends up having to do more work. + * + * Add other values here as necessary, for handling packet types that + * might show up on non-Ethernet, non-802.x networks. (Not all the ones + * in the Linux "if_ether.h" will, I suspect, actually show up in + * captures.) + */ +#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ +#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ + +#endif diff --git a/Include/pcap/usb.h b/Include/pcap/usb.h new file mode 100644 index 0000000..adcd19c --- /dev/null +++ b/Include/pcap/usb.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2006 Paolo Abeni (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Basic USB data struct + * By Paolo Abeni + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/usb.h,v 1.6 2007/09/22 02:06:08 guy Exp $ + */ + +#ifndef _PCAP_USB_STRUCTS_H__ +#define _PCAP_USB_STRUCTS_H__ + +/* + * possible transfer mode + */ +#define URB_TRANSFER_IN 0x80 +#define URB_ISOCHRONOUS 0x0 +#define URB_INTERRUPT 0x1 +#define URB_CONTROL 0x2 +#define URB_BULK 0x3 + +/* + * possible event type + */ +#define URB_SUBMIT 'S' +#define URB_COMPLETE 'C' +#define URB_ERROR 'E' + +/* + * USB setup header as defined in USB specification. + * Appears at the front of each packet in DLT_USB captures. + */ +typedef struct _usb_setup { + u_int8_t bmRequestType; + u_int8_t bRequest; + u_int16_t wValue; + u_int16_t wIndex; + u_int16_t wLength; +} pcap_usb_setup; + + +/* + * Header prepended by linux kernel to each event. + * Appears at the front of each packet in DLT_USB_LINUX captures. + */ +typedef struct _usb_header { + u_int64_t id; + u_int8_t event_type; + u_int8_t transfer_type; + u_int8_t endpoint_number; + u_int8_t device_address; + u_int16_t bus_id; + char setup_flag;/*if !=0 the urb setup header is not present*/ + char data_flag; /*if !=0 no urb data is present*/ + int64_t ts_sec; + int32_t ts_usec; + int32_t status; + u_int32_t urb_len; + u_int32_t data_len; /* amount of urb data really present in this event*/ + pcap_usb_setup setup; +} pcap_usb_header; + + +#endif diff --git a/Include/pcap/vlan.h b/Include/pcap/vlan.h new file mode 100644 index 0000000..b0cb794 --- /dev/null +++ b/Include/pcap/vlan.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#) $Header: /tcpdump/master/libpcap/pcap/vlan.h,v 1.1.2.2 2008-08-06 07:45:59 guy Exp $ + */ + +#ifndef lib_pcap_vlan_h +#define lib_pcap_vlan_h + +struct vlan_tag { + u_int16_t vlan_tpid; /* ETH_P_8021Q */ + u_int16_t vlan_tci; /* VLAN TCI */ +}; + +#define VLAN_TAG_LEN 4 + +#endif diff --git a/Include/remote-ext.h b/Include/remote-ext.h new file mode 100644 index 0000000..35a2fff --- /dev/null +++ b/Include/remote-ext.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef __REMOTE_EXT_H__ +#define __REMOTE_EXT_H__ + + +#ifndef HAVE_REMOTE +#error Please do not include this file directly. Just define HAVE_REMOTE and then include pcap.h +#endif + +// Definition for Microsoft Visual Studio +#if _MSC_VER > 1000 +#pragma once +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + \file remote-ext.h + + The goal of this file it to include most of the new definitions that should be + placed into the pcap.h file. + + It includes all new definitions (structures and functions like pcap_open(). + Some of the functions are not really a remote feature, but, right now, + they are placed here. +*/ + + + +// All this stuff is public +/*! \addtogroup remote_struct + \{ +*/ + + + + +/*! + \brief Defines the maximum buffer size in which address, port, interface names are kept. + + In case the adapter name or such is larger than this value, it is truncated. + This is not used by the user; however it must be aware that an hostname / interface + name longer than this value will be truncated. +*/ +#define PCAP_BUF_SIZE 1024 + + +/*! \addtogroup remote_source_ID + \{ +*/ + + +/*! + \brief Internal representation of the type of source in use (file, + remote/local interface). + + This indicates a file, i.e. the user want to open a capture from a local file. +*/ +#define PCAP_SRC_FILE 2 +/*! + \brief Internal representation of the type of source in use (file, + remote/local interface). + + This indicates a local interface, i.e. the user want to open a capture from + a local interface. This does not involve the RPCAP protocol. +*/ +#define PCAP_SRC_IFLOCAL 3 +/*! + \brief Internal representation of the type of source in use (file, + remote/local interface). + + This indicates a remote interface, i.e. the user want to open a capture from + an interface on a remote host. This does involve the RPCAP protocol. +*/ +#define PCAP_SRC_IFREMOTE 4 + +/*! + \} +*/ + + + +/*! \addtogroup remote_source_string + + The formats allowed by the pcap_open() are the following: + - file://path_and_filename [opens a local file] + - rpcap://devicename [opens the selected device devices available on the local host, without using the RPCAP protocol] + - rpcap://host/devicename [opens the selected device available on a remote host] + - rpcap://host:port/devicename [opens the selected device available on a remote host, using a non-standard port for RPCAP] + - adaptername [to open a local adapter; kept for compability, but it is strongly discouraged] + - (NULL) [to open the first local adapter; kept for compability, but it is strongly discouraged] + + The formats allowed by the pcap_findalldevs_ex() are the following: + - file://folder/ [lists all the files in the given folder] + - rpcap:// [lists all local adapters] + - rpcap://host:port/ [lists the devices available on a remote host] + + Referring to the 'host' and 'port' paramters, they can be either numeric or literal. Since + IPv6 is fully supported, these are the allowed formats: + + - host (literal): e.g. host.foo.bar + - host (numeric IPv4): e.g. 10.11.12.13 + - host (numeric IPv4, IPv6 style): e.g. [10.11.12.13] + - host (numeric IPv6): e.g. [1:2:3::4] + - port: can be either numeric (e.g. '80') or literal (e.g. 'http') + + Here you find some allowed examples: + - rpcap://host.foo.bar/devicename [everything literal, no port number] + - rpcap://host.foo.bar:1234/devicename [everything literal, with port number] + - rpcap://10.11.12.13/devicename [IPv4 numeric, no port number] + - rpcap://10.11.12.13:1234/devicename [IPv4 numeric, with port number] + - rpcap://[10.11.12.13]:1234/devicename [IPv4 numeric with IPv6 format, with port number] + - rpcap://[1:2:3::4]/devicename [IPv6 numeric, no port number] + - rpcap://[1:2:3::4]:1234/devicename [IPv6 numeric, with port number] + - rpcap://[1:2:3::4]:http/devicename [IPv6 numeric, with literal port number] + + \{ +*/ + + +/*! + \brief String that will be used to determine the type of source in use (file, + remote/local interface). + + This string will be prepended to the interface name in order to create a string + that contains all the information required to open the source. + + This string indicates that the user wants to open a capture from a local file. +*/ +#define PCAP_SRC_FILE_STRING "file://" +/*! + \brief String that will be used to determine the type of source in use (file, + remote/local interface). + + This string will be prepended to the interface name in order to create a string + that contains all the information required to open the source. + + This string indicates that the user wants to open a capture from a network interface. + This string does not necessarily involve the use of the RPCAP protocol. If the + interface required resides on the local host, the RPCAP protocol is not involved + and the local functions are used. +*/ +#define PCAP_SRC_IF_STRING "rpcap://" + +/*! + \} +*/ + + + + + +/*! + \addtogroup remote_open_flags + \{ +*/ + +/*! + \brief Defines if the adapter has to go in promiscuous mode. + + It is '1' if you have to open the adapter in promiscuous mode, '0' otherwise. + Note that even if this parameter is false, the interface could well be in promiscuous + mode for some other reason (for example because another capture process with + promiscuous mode enabled is currently using that interface). + On on Linux systems with 2.2 or later kernels (that have the "any" device), this + flag does not work on the "any" device; if an argument of "any" is supplied, + the 'promisc' flag is ignored. +*/ +#define PCAP_OPENFLAG_PROMISCUOUS 1 + +/*! + \brief Defines if the data trasfer (in case of a remote + capture) has to be done with UDP protocol. + + If it is '1' if you want a UDP data connection, '0' if you want + a TCP data connection; control connection is always TCP-based. + A UDP connection is much lighter, but it does not guarantee that all + the captured packets arrive to the client workstation. Moreover, + it could be harmful in case of network congestion. + This flag is meaningless if the source is not a remote interface. + In that case, it is simply ignored. +*/ +#define PCAP_OPENFLAG_DATATX_UDP 2 + + +/*! + \brief Defines if the remote probe will capture its own generated traffic. + + In case the remote probe uses the same interface to capture traffic and to send + data back to the caller, the captured traffic includes the RPCAP traffic as well. + If this flag is turned on, the RPCAP traffic is excluded from the capture, so that + the trace returned back to the collector is does not include this traffic. +*/ +#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4 + +/*! + \brief Defines if the local adapter will capture its own generated traffic. + + This flag tells the underlying capture driver to drop the packets that were sent by itself. + This is usefult when building applications like bridges, that should ignore the traffic + they just sent. +*/ +#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8 + +/*! + \brief This flag configures the adapter for maximum responsiveness. + + In presence of a large value for nbytes, WinPcap waits for the arrival of several packets before + copying the data to the user. This guarantees a low number of system calls, i.e. lower processor usage, + i.e. better performance, which is good for applications like sniffers. If the user sets the + PCAP_OPENFLAG_MAX_RESPONSIVENESS flag, the capture driver will copy the packets as soon as the application + is ready to receive them. This is suggested for real time applications (like, for example, a bridge) + that need the best responsiveness.*/ +#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16 + +/*! + \} +*/ + + +/*! + \addtogroup remote_samp_methods + \{ +*/ + +/*! + \brief No sampling has to be done on the current capture. + + In this case, no sampling algorithms are applied to the current capture. +*/ +#define PCAP_SAMP_NOSAMP 0 + +/*! + \brief It defines that only 1 out of N packets must be returned to the user. + + In this case, the 'value' field of the 'pcap_samp' structure indicates the + number of packets (minus 1) that must be discarded before one packet got accepted. + In other words, if 'value = 10', the first packet is returned to the caller, while + the following 9 are discarded. +*/ +#define PCAP_SAMP_1_EVERY_N 1 + +/*! + \brief It defines that we have to return 1 packet every N milliseconds. + + In this case, the 'value' field of the 'pcap_samp' structure indicates the 'waiting + time' in milliseconds before one packet got accepted. + In other words, if 'value = 10', the first packet is returned to the caller; the next + returned one will be the first packet that arrives when 10ms have elapsed. +*/ +#define PCAP_SAMP_FIRST_AFTER_N_MS 2 + +/*! + \} +*/ + + +/*! + \addtogroup remote_auth_methods + \{ +*/ + +/*! + \brief It defines the NULL authentication. + + This value has to be used within the 'type' member of the pcap_rmtauth structure. + The 'NULL' authentication has to be equal to 'zero', so that old applications + can just put every field of struct pcap_rmtauth to zero, and it does work. +*/ +#define RPCAP_RMTAUTH_NULL 0 +/*! + \brief It defines the username/password authentication. + + With this type of authentication, the RPCAP protocol will use the username/ + password provided to authenticate the user on the remote machine. If the + authentication is successful (and the user has the right to open network devices) + the RPCAP connection will continue; otherwise it will be dropped. + + This value has to be used within the 'type' member of the pcap_rmtauth structure. +*/ +#define RPCAP_RMTAUTH_PWD 1 + +/*! + \} +*/ + + + + +/*! + + \brief This structure keeps the information needed to autheticate + the user on a remote machine. + + The remote machine can either grant or refuse the access according + to the information provided. + In case the NULL authentication is required, both 'username' and + 'password' can be NULL pointers. + + This structure is meaningless if the source is not a remote interface; + in that case, the functions which requires such a structure can accept + a NULL pointer as well. +*/ +struct pcap_rmtauth +{ + /*! + \brief Type of the authentication required. + + In order to provide maximum flexibility, we can support different types + of authentication based on the value of this 'type' variable. The currently + supported authentication methods are defined into the + \link remote_auth_methods Remote Authentication Methods Section\endlink. + + */ + int type; + /*! + \brief Zero-terminated string containing the username that has to be + used on the remote machine for authentication. + + This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + and it can be NULL. + */ + char *username; + /*! + \brief Zero-terminated string containing the password that has to be + used on the remote machine for authentication. + + This field is meaningless in case of the RPCAP_RMTAUTH_NULL authentication + and it can be NULL. + */ + char *password; +}; + + +/*! + \brief This structure defines the information related to sampling. + + In case the sampling is requested, the capturing device should read + only a subset of the packets coming from the source. The returned packets depend + on the sampling parameters. + + \warning The sampling process is applied after the filtering process. + In other words, packets are filtered first, then the sampling process selects a + subset of the 'filtered' packets and it returns them to the caller. +*/ +struct pcap_samp +{ + /*! + Method used for sampling. Currently, the supported methods are listed in the + \link remote_samp_methods Sampling Methods Section\endlink. + */ + int method; + + /*! + This value depends on the sampling method defined. For its meaning, please check + at the \link remote_samp_methods Sampling Methods Section\endlink. + */ + int value; +}; + + + + +//! Maximum lenght of an host name (needed for the RPCAP active mode) +#define RPCAP_HOSTLIST_SIZE 1024 + + +/*! + \} +*/ // end of public documentation + + +// Exported functions + + + +/** \name New WinPcap functions + + This section lists the new functions that are able to help considerably in writing + WinPcap programs because of their easiness of use. + */ +//\{ +pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf); +int pcap_createsrcstr(char *source, int type, const char *host, const char *port, const char *name, char *errbuf); +int pcap_parsesrcstr(const char *source, int *type, char *host, char *port, char *name, char *errbuf); +int pcap_findalldevs_ex(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf); +struct pcap_samp *pcap_setsampling(pcap_t *p); + +//\} +// End of new winpcap functions + + + +/** \name Remote Capture functions + */ +//\{ +SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf); +int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf); +int pcap_remoteact_close(const char *host, char *errbuf); +void pcap_remoteact_cleanup(); +//\} +// End of remote capture functions + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/Lib/Packet.lib b/Lib/Packet.lib new file mode 100644 index 0000000000000000000000000000000000000000..81618bc8ca221dd99e52b0289ae4692a01f63d0b GIT binary patch literal 8450 zcmcIp&2JM|5TAVT(Lh5&zQ0XCKra-6O@f4?d{QEu5ZR{XW;XVwEVaE`+o450^v0>; z#EE+)#EAo!-Z*ih5|!wsQV*5*6LR22XZG#BuYFz|oUAnQ?##UTy^onU^IlF}v#N!A z^Icyj)aTO7>}+x_nY?^a<%ge_X6J&}yIlaV4A5N#=y?jz`xanip3%@Q0MWo5Mk6nf zk2Ly$(bzTsQQtJ9;Vl57{!T`tlcDC1hSB7G!G_lL5|2_8W zeLlaozIiK?%df0vmosadxm%gMXBB?1>yXcH+)k&Xa`=@gFmdk2?TwrHmBrj5 zX=kfyRUdw9S8I-2K{HO-%Quj*7T?%)qqfZFnk!Z3fn9B+uSr>}Tb^B6ELvXOuBzQp z1WY$;SBmUSRh9X=X04Gb3YhLQ%ui8etGM{N??1GJx zXELjan96d=t=YzjQzcA0Yu9i5bloXMW?2z1U0m6~?uJ#iwfTz(S(}XgeTf`K}AU-_jawa z?HWdg{S77D_PSHaxy!EiP#qn0LP)r68pG-XyP}N_XHAj>w(G9PLSM5i@p&&+cFG>P zou+Eng+S3wJ7q^-V(c<$ha#rGRwLeOrDzvb+lqkOPP?wRghE*w+w1GsVd(W3wdA>Z23A5S_@U#cuZ7;z52*BRG<^~oGiSrXFB&_VNhyMKiK$qTqDfO% zVoHIo z{sJUkwc~FuW<+|r#GuYYU(XR_kvV#(o9AAiTz3jpx8`ow6X!EuCDLmfH+`x8hrp;M zS-`~azhRYtiI{i-1-npi%O3Xi-0NT9BleHsQ44ygbL|igc>%!h?!A!X4~|-Ngq$Sl zAOpBgN^ppewFH02I=~$NCag{T+RuZpG^Ne?$xJG|n;<`F_x=f%P82zLu4S`-I zKe-%=$WML`KZ0NK6SBMS>)*_(V%3H--QXTUxJZ+;Hai=MuD=l=Zq{O=D-}$T@46IS z@*_o8GUSuP{uO=%zxz77&CDp)!woNB_UwnL%i)a~-HcIarrT)GI^~bMx=Ql@mJijb$1uZTrjw)!P z*lh(b8E&Ln6ygCLs{|f}{CLp<_qsWT0x45xRYXJ;;<-I9rxnDwN^ep@3~!MR3T;Y46L(WG+W!N((TxBA literal 0 HcmV?d00001 diff --git a/Lib/libpacket.a b/Lib/libpacket.a new file mode 100644 index 0000000000000000000000000000000000000000..4d49131f09675b3588d36e98512446248089a84e GIT binary patch literal 20814 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 E01M8b=Kufz literal 0 HcmV?d00001 diff --git a/Lib/wpcap.lib b/Lib/wpcap.lib new file mode 100644 index 0000000000000000000000000000000000000000..f832e0445b5c7dafe5a0f887262064265ba2b293 GIT binary patch literal 19320 zcmdU0O>9(05-uRW4k3h)gbS|j4}Uz#t>t&Y%Y)S%rl;4JTp5pCTuoh zr6{Lp73IKQa^QrbD7U>tdx#=VoK`4`_7Fwch+_^p| zS6BDfKh<5`)$NUA`Fde~ylZ#2`x_V>8Oe?2azi67zdwrRhWbZ!@NyRb{1af)Ai(As zfGr;YjQ*f$?=%3>0gMxkW0+_MhKYuM)O4~3fN1X*n)>bm5FNa!=@dc`jeVwR->(2f zr@qm2dK`f0IF?CtuvgRRPmzx_yk67JDF8*E;}+y2(SaM92H(eeAPs$`X>=9#JJEGb+ptfFdaw>e+iq*x`Yiy_8LSsk zPhHb?lqKrHekD4vPE+rP*gr_)Sa+hqpEMo(8T%RO(3GZ$?*SB*@r$$_@rd?+t!Zo@ zjvvxV99NR-y@~cP&F-(99u}FD=x9fbs_AYW3=3tr%W|L_8r?37d|TiVLY~ zEvy9P(zP&>67jfHb#AU)syKqo60(JIwPA#!ut>cUHY?T2Y`I#v8X-CyA=yYsIvgR1 z{ec~7x`z7J_EoXesO6i5`6%5+g-kun7o&6?6lsXL*a+*hi*s{fJrauXgmly&fhf^s z3E4_`-3*NminK;E-)tB(9ge34;dLpXqEaSo&XvkdS!WX$Qnj#NubO6=h)8cV>r%o- zg$#}#%0zWB5s_Y~F4Rh8)(8_zN(7avo*HT-qioC+@^ketEL4l8NE;P0#e6f5+G>={ zMukkHlCL%9t7a5zRLGPXLF4Uwtri*$kqjZXkpEM)9$X9SjZ(D|bw-RQrAw8hw96CH zIB?un6^x}-su3UwQbeQ|7jfC3NgauZcw9Wn7pTvr=@psX2-v zPKxSZ5_y=PnoE_N$P;l>b2RJuN@JnKx{58)m9q&VA1x?B*qjgRVZ~&L@q{!@+G|$O zMuiOZZ>iF(7OFMdSjiHy%7zr}qAKD-YOYi%Vt=CNK_NilssaMOp#PS~G0a z3#jr@2fHjGTg%rQuBgKil48`YO^Y*yd_7#KHp6_O8RQFvux8Xd$xh8vE;W)lG&5CR zE{YZ+xiZSMFke|T8koY@IHd|2`Ept5435N=dVT?dRHmqxp2NoQb4*B>?S@fVJ*ax; z#(FH|ab7p_3pF&AS9m=V93eT2qkmOwwM1MS~zh^OYj+6w(qzK0R@x9xxG+j`OQ@O)YAwXd4wWGVhD0>`FwwU%QhSy`Bsht3yBaP(% ze(43MpxoU(=zYOFA0d4{0I&g1$&>iKwHu(>kDdVhcHwsomUZU@z?X;clzbTAW2BWw z0NzC^?gw}e%b&*d8(7cxv5c>V09Ik$uOp3b!!mjRZsYgctytC>fI7y0LnY-36{Zf7=>XNfjzJf#$Z1jgA3T1 zPr!??4yIuo*25%B!C6=dn_&}df!E4q}7h2Vp-SXex#;FLwXebxMB`=5C9I^9PV8s(AynihM(q47Hlj}F34Yx5 z5?lA>X`GVyBqM>5uRR)q`2Y)!$;$8I=7z=ffJr=%AQ6tTb`!4lnRETAExQINoiFRt&`kb6)zb#5OTk(WXF`3LzuyeV>Hxo-xE;5&wx*TA8>_O0# zmkw~5!tPVa^mwZsG2>X)OyT6P_FY#r`5rUt)woR?W2mOSw@rK-bJ`>O{@4yI%WYCPuf1kaI9X$ zNp*3w@H`qDr?aNs+&Oo2#&Hz0Why()^2D`)wqGhaX1N5xvM@q!8M4BUSkpzx4EynIBam!|lli=G+Mk}qYj1rahg&EpnNTMnkZ z6X;YS&b;BMmJ!Md!^x2hGOWz7`V;-AEPO=~#ptsfeItO|rMA!b4@pA?b9;q` zELR%xFI6w99i(bVQ6qBcEkNU%@)Gd`Z&9+L3Wy4UP

ii zt(#u7(X5F2?uWKuz$0Zt8OJO)7!6qr;{`-mW-ub#&;iOoI?D5ivfQBP z?YRYMzrS+nDC-bqg+bw+zXjsZL+Gm53p&N2Dhi z$sPL*kF_J!=aoO!_oo5a`Wl^G6SWCz8X~fDnjyWbFWEo#2>_LrfpY(yof(M2GuM&^&tn45RTGq64I1es%5IBD_q*{iK;u2FYa$7I6mN$G=TOG^=4LlDoZVf8Mpw z8`>v5@1R6iH=VS|vlhSLVkx(ih2l^~-N<=b^b_Mk`m)-zIUnA+Dq&#M2jgMyAnZo(4AD0*qp9b5G`pq6hCIM%0W!8nw$ z=X{p)l{75!sW3?UZSYVll0YzKHbPhY;~;29sU1>;c0zRY>-XSai;ZdbYZCG=to zq}NeHHZoF*W4Z&(oJadNNlfy2cY@QRZM55d-N8}r^iGgGy0KXzv2)UbaVVp147WIa zljmv+W4U(#jA6SoBbv{mY-KcYyI`T(Ull#QxQ(&o#f1gqP{wxoR*xQmS-4#qq68LyTjtHxo|KwtJB;6=J(Y@(tt?ceEx_3CTXEKlvOJ{NFy#1JrL^z$>YNrb&<@)H8*An#mck^5f7vwn&$@= zl0z9sY}m8Q7*9o{hYgHkLs{`!j2wCD52oO$CleN~{Wa0!-I!oJ@fxE8EMH$9a z4-3eltbOUxmcuD{<_QVov_E zH5QOV8As7)wU0Y^#`|rDZ3!!<#~Pe)(9AO+ZjXh{+AKB$jT}9S zHBXm1z-lq?X#aRxVyQ<_9pHHO31=jZeQssJ+SYsD$#znrB{r%InpH16?K&%w>>DwL zSc1lTMr+DJqo>Fgh(lRh<ClhvL%JxCqKEC=kR{Bh7JE3l N*?W_a?2Af)@;}RMSJ?mn literal 0 HcmV?d00001 diff --git a/Lib/x64/Packet.lib b/Lib/x64/Packet.lib new file mode 100644 index 0000000000000000000000000000000000000000..30c15405037e32d45233593bb67ac27f66c5f3de GIT binary patch literal 8290 zcmcIo&2JM|5FZl0Y6v88zCR75f?g;IHVINyIbSpZCs?*=Z=v-t?kgF9x8D_ z2yx3zRz869X45Mw7WC&=J!5k-pqR^zA_r6 zt*>KWCe&wkZhk(wkW8hLD*v4KJ3BvnfqveI0l+dq&s%`rF@U}-z{pcd!&?A^23}Jd zodO^TJbHp}q`}LSCb18MhGr;Dzejzfi8~(k;~QxNu?S6Gr!;`$ z8mJVD>l@jtxk7PeHNTu&&laxcijGnG!E8aXxOpv;@yp>?szAkswQHMKiYtqSMaa7C zGSCIYSk_oEi=HXCZZbab*mMiE3v8cQo{_gDY2&Ol2~}9G+D$WPsuTg$&6};YpDtNdeO6^Y)x<^gY;GDgQ=KtgMqA{}AGggWaX#TT zjK|TJwks7y1Cd>x&~W?;$l~&R-EcCd>YZqWv8f*RFyAs-zHU>dsyl*+vC>N}nX>Y63fc=w2Tz|tZt42}L<#CP8?b>$@tEDXfstJMI%k_H2 z_Kyeq=?S>q4Xa+Tmu=^^GA07g1>7#_`syvSu8s-kNeDc9bBJT9 zie`)R70rxQv$Vy;t`c2OLbcbMgtb~Pn`OnW%;$D9w(Z=+ZEwlkGHzM6y33$-pPaTN zzHc?MCBs=W>gB3B0H1K@`EHKqyHf!0wg)p;FF>{r;OPj!)-b^90n8$!02RzEFOeQ& zhIobidvSmV$RES^n+bq-`2K+ZkH!JgNcX1!9u6T7>DeIa;r|k5$tOsc@&DB%zzoXX zBmWMz??*}_=5^#(5EGLx#NZelg78NM9i_hl_>({}ze8I(Ho?U^T1YDA<^mKiZFmgLP?-MW1N=>@49 z=**-@K%CPi)UdU_8xa=SGkH^e8$^Y`omNc`d9fpXN);t%IISR%MT#%V0?8v~w(gg@ zENt6ReN<~HJk>7<%4@=FHU0KE)t_{jAIl~rZ&uiC_HE6xW zMB%t6inHZvmFB?zJLx4a&U$CQ8?o};ZixLelR2^vFq_clY}vfI{rybd`-MgPz$2UT zq@j1|tW`Ez#>^ZOVB&&5kpA@;-S|$!x#ynV1&@tc4d5dZ(8-bc7IK;v`>~Dc!)&Umy zKf%i4-=}Pu?c79~($4&3F2(K!h@W6JfqwEkc8YcuAjnV3(h$g}LGRjj&jrrVt3g^P4JYYQn~bp4G0VY3zzT`6yZ+~_3HB|m9Mv*=1PK8fPL z*pK%ndb>Wmoy;uOwQVm#cI=O-Id5VUp20ltNwCOMifP21hg=JIi#Vvm1hL3ut+#8V~KJ$0$S2LmnXEi z!vi>02Q2RR`7R1lNX*52NLYC95!MkIi&gV`gmsi-Nk@bzSQmr$5|UJV1eSE}kid4n zkA&oaV;oI8%}9Xav$I_!88W)#98W$X2?V)_?F=o&3Ukf#NvYV&(bR)i6ucxWRWv-j z%EC&FMH;)hh=ddpY5g2YoCYIBTF9Ek1F)Jh*p59i*(T56bspZsu{re4v(glb&(3@! zY^??r9CAn&)_2znhh!w_=p6+wEXIa)JoiAqS7;+L8owrpf|i;O-W@+j?--TQe3uWA zP}#vvi)&K3j>)LvN<)GZpV2*YTtOrEB*OY9cxK4BCS)XW`y#=K&(6jbcHWqj(c~)~ c0V0jGGrX{hZc4^u_e6VyH7&5j<&%WA5NCuq?tv3xC4^R-P!0$og!X`Ph*m332ysA&6DQcOU)9zB{PXiq#vFE}^50!u z-CsYdySl5}>kkwf#o0s4clC$Aq2WDya^tztvEh)v+eULkBRLa%aXAtFlc@g|(T1Oi z1_p`tOk4UAU(kW)E#1TywDm1RxtE9(op{~Q_!yC*oim0Gp=d=XUofP%aqtxFU1n%E zc$(hF7qsiPp+VF`(XOS2c7BEWg9d@CXtH7G#9FiyG%;Z5ZG08|V(3VnNYgj?g7%_( zMY%ph$3H~-K>L3%G_-<9(ePD6V{Z~^LL0|G8^1TS=_4Xd7w`pbe8bS`S@aKRGwP)2 zTYMG0W@!K4fdkq;Xy_omiiTb_bf|>!0UiF>(g}P)hrTwnWr|4AzEhS^_kExfKN{M) z0pkH0TVv?xO(I1*`YfRhI}~BO6^(viXjdKm3>w*HXxrx)PtcZ63>`xqHNA^3XmY8c zqZd&=D0j=yVPn z#${?VGnH~xiaCyuELLhwD`J8o?RwOz)~eH$TJeU#k_;i&jLS)ekV6mDxnZg=xo=A4 zX1&lV&YDz?35iBjD4A3p5-9+iZ$^#j`I(uhVMIcfkhL9<_oW3KAz6)Hv4fyPBBj|X zw3-%22jgk^=oOKyVp1Y%&6F!GQBxZgGWDp@sM)62ut;sT8X}!zLIT4C2~!6f7OBPB zT)kXzV{Rku>G^6+YrIxgo5%5l^h_g)inWq0j$=ZiRA?2ThgO9+CM25GLcKX#v;EC6 zAyIDTn=cpY^~maghzqH?!b`PAej#c!%eAU$e#nxtd*7he3UO#u&_9^YDqE^Gpo0y|gZnnv5UZS*E(tPo4>zYi83e|b5U+rvz zQLe)HRw{Wl!07nvjlvuy@kEWxEVTyNMMS!@|5hE%_?eB9Q&Ay}S=ubj)nU%AyH&+A zgxoa7`G()__^6OkeV2FU&`#4BPe=oqg&Ep&grqAnUoFfFK$%#d|*eKUqY=;@z79a&gIhFEsXul;x$Qc9HiYl&U+8*k-kixvp7s?Girn7}= z$yxCPzQ|=}q-nPei&V^=@`75Lbg3K@5^AKNoO9(STQ|>$$*H{#b`7R5mC-R`a;@@Q zRGVkB)XFo|LkjT6o6Lk&UYw6{27L!~;yBT}2ayiPz&7>Cw29~f=naHtQJ=M&iM|EB2Kqbl45B`RsOzi9TS9w2K8*TeiI6XY8_hW3G$qW%{le+%`zjqrEC zfh&jZqCOg@QQAXew2uzZemYK<&|%ByF?yUXqHEXC6rH29^Z*Ue2HHr^&;?pgPg6e) z(F9$iVS0j|qep3w?xW{vEuE)lX@qigH{C<`(^IsLR?<#7K_}@UI!L=|Dcwtl>0x@1 zj?g3>qocHiw$e7*PCMw2bcQa|6}n2B=ufnocF|^9PN!%EJxPC{RrCn${XeVi6715u z_BEL47Te{hs5N3fJ-$7{>T5PuMx<@i6W`9X?R#OC*V${Vkzl-=xkq> zJ8h$!-3bmu_COjKNP+9}D8s4DUNu>#nD^#kjI`X z!46HFNJ#b{bl7Zk-jwm_M%Is+!I4MXSMLH8burAClTYs*g-}L6hR6|jTZPBfm|$#N zW1C_j>{8-RxVW!w{MX3zNsgS{Qk(e*Z8IiKjQ%P>El5J9aw@(dlesdQZN^7T4$ntx|kRRA5 zJtI&F@Ae3VwX-Q?CCzb^z(=xBT6l$Jf^#-XUPnJ}AY}cnhUa|=Y>0&kRzdHjV6!U> zt~a&9$R?TB@Sve%-lXcNIBPpIYWT*v>%6vxr#%qzbR)eHtS4I5#R5YarX-liL;-P> zbY{`Wu}?|cEYT8g1X<8}JViWq0@SOJPS(bbL*7N(*)8V1C*f(u6e7$SGrZe7!Gvik zYrHrdxByw1b#iE`G)ovM9hc_GGLk$9$)38vDVKB;B`=+<8zwY58WNK=?l#wkMkj_Q zMzE~=#TgH~3}^wz)2q1Qu*2LN5l)C-d^y9R1*FL^3~^E70lX1Nc7i$6po{RrLN`5A zn*%K<3<1Z@I-lt1c8h;Z*7&AlOkVIUKRnVi(@j_JO>oOQi#YcJGn^m6F@hTazl^(N zIdZ|$?F51wf>6WdLhZ!G5^Y#+aW=8oO&d>9zVz~oaEJR>eJeXys#L7g;WuA1jE~uO z4KFS)y|IK={dZt$*J=pJT=x@88%%H)&`G7-y+dXD0+Q-ADFm5!GqHl{v(@HPa77*AB~B zJ2K9z;qejCYi3^k2MA_-lY1>?Ucr-PerjH-c_n`nG~wnIyjkX_ZM<7wpsAe{@qkg+7`Y)kbTN!afN>Yr4YBR* z1;srmi=n7_(PvT2j@pB8D5DnUoD#+`9s|Y_i@|$!E_`u&>~7$QV?Zw`DLr=`F58E) zqU=dt-_>4!tw-_hM-SC?ucg_?rxLJ~+r-0hC}Ydg(AF_p=rd&ukDC!Km(q_z!#6-T);pX87c%2?--{_azd<0Xo7EmrIYAt*q!f$4Qy@u)6~krKh?F@z=rF>&Er#e`E7%u{n|KDpr#O>SelUfI+3F(VGg-A3AbF5fH; zW$nY1^S*lfsC`ThOS0WI+c07Kg;vqS-2xEBMBZjYE7~~Gt$&~f~gv4|HWUrMzl(B7T z=dY6z%RSlYrClku=9I*8uYrCitkV+9Jv8bCtHYdAJH=-OmVU$33r>n&oDn$wd6Wm^ zP*%N2*;h>othNnl*DF>orB!DIl7EiNZRuXyQ%35X#8UrH?qRxbQ;Mda7dVUJ8A`#r zAh7r`axd*lIl(?Bu*CDZUeG$M^vaKSk$M_k6<<8GwYF8!R&T*-0nuNr4^Sy8A`n5bmRqT{ zK5o_G(b(u^yZQ8UkW5(>;x9{lDqk(~eD_5>eNlDqb zapUaSv*o2vfswy>543gya=eTKJ}bJsb08RyLkrbzg~EDF)&yx{%~3lMOmjI z2r>fq&!#BLn;*SDdg=``Ge%vn(_ zHtGJ!s?^=xQ)VolXES2J@MURR$8V^WUk}@~H&O9u;)XhDr?A*8NV1jpnGS9@R3zjJlMS^bL*v(^3?X@it_xf^eOAIF1)HHQBqYfeohaonv$Cm)jId+ zOVxIDS1y%GYM&OxMbuR%tEwZv6c&U_detcl+-(L0I+vtX6%TS(6-esN{F)w7bMOD| zOWW0^33nGuWA6=U_k~Z`_8H2%Xi~K^>vZ`oLJj;+dof+Rb*dtUE!B9(#yAE zinCMDvqwpLLl>`DVqzVqn&SNSS4zywZ(O!oQ5+P}ZqDo*iQywp2?H;6m*1FM+v(ik zKuPue2llH<lpzzQC0ZQ&fW!@2| zCA+sBFDXoZ&s`OJt!UeG*-;nSw@IqwS!bgXV{4brPy0l^ru(7V((LEr;MieH9$eol ztF#|gWOnaxM#TNAhX?ycZV#28>t6U2vUhev*6X=!y^Cyctm@*mSw&||2b89k2T12S zs5WPQGwMKAfV2p*(!)o6B2$E!rv#ZHO0PlduB^0pWIyVm*{I^DzUzC8eCW8? z=BFT&pQ;pzy=-=tzc!;ZH7GzD1dQ^-Q+y&NpT{jR`AMZnyl1oX>1)aw`%wjE%C9pb z{^#7`jy{pUx+;`bicdg?AKvS8+Eg+s!X*4ofn?BwTUi5A9Wt#IhcW`Cp;u~zX&I+$ z6~0HjCOi(CTN{<%GdDz;c&lIU&Wcl8MG?v_mEWu%n^Nd_qUfnFly0f|W~(eABVuOa zHt$DAeIrLYsMenG_dlE&X7MD9CeFz(_lc}g7e80TZeW2VbJE?B}+N|#LT|(2( zeRDEXggcomlAM-B22c?h3dcL19#xL@1NIL`g0pN}geW^Eq)M@ob3!R1?5(+j=DA*LC zV3UM`T@niRQ7G6ap=dbWwdHjEVHYQI*zzS;6X*qvTp*H2$8BZXM#u$!2E9%Fh1%6;Y%r%wA8iWl z98b^o;Ggdw>_>fXfwbF2~>0cDCW+zQ((`ySCnlYPFH$mt-V0+ra+gMv`S)y(N zzHo($)~+2>oIqd!0<=ro(PThQOSiSPHaGc$z!WPPc@uMMn%q|1f`-LXNOZ8o+V&d$ zHbOdUt0AU!(s0v=VVEv*Gjf(>GO3|6{Q{Q)GvqyDTfmceS{Wq=e`Gh$eZU|X;za!?7xDpmeE6|Pgz zO(KB$bqcOc$ko6)h3u!3J#_Z|c~w;vk-}r%1H1=XsRz{S6idd1hFIc6slF`L`S$H$ z_Qem5dBRTU+4*M5v$Vv$1lR_!RO^Ee{bum6-?p7PZwYA&3)o0e=P64|GczkIGcz?g zm}G@1OG_)XP72S0O#vA^OFoTl;6%6?2%oWZ{~SOKoe0-?^3!~m`s8OxPXB*&n$|r! zzi?BOFg7FVyr(F+_`6=-k&dIk_p|sgGQA|=!w(|Opl0qnzSh@!9ZyqEy{Yv2tco;$!c%1qB5Tm(zT#t*z(Oo{29hzP~WMW9N6j>acU@%{>PyiVK%J zDchX)@#r((N^0@uwz&3goBq}L@|RNv?D=_=P56?Hecrw4KYY=F^rOd%qNoY}|604$ ze}Q1wo2CUpqsJY2c6ZpK$LU8Zind-HYv;EpX3wHx!Mu)9bu&)b-#Goo@8>^%ZpR_-A8pm9le*fP%dwWrZ#%gZ4hgNPEP0ZX zygWHODX{cO?wRD|B?TXp_YA&WcENAcr1zm*!sT*wSXgN+4}`x4Onbu4m9C6a zDyzzKE^l|)9veNfwvB!H=Ueu>hE~Q`J@CK3rl9l8;eQX$AL67e-=O$nb3yrbm%txm zqqqN!a-0`y@A|0LF6XUF2Y(!J;{4dWim&tj-qp-=psii`?^{xRtLDC)WM1xF(Pdh} zo&nW%Pm{OJ7Y(}+?6yGe^278sU;bRy{@{{)8`rzbhg5njp0L%bE_!K#u_ZcwBlk$-$@-sFG|l`h!> z9(?Vda99`_HgTY$d(`wb0ljO-+CANOJbJb4dX!}MowsHz{C?8ouifJug^@uv*qA)| zn%nN4b%VBaGj|$J^Z1&Dy*5r6?Cmc)u?6HlOfo+czNcs1sY|Z5Gm2$_`_D~ZbHzQi zLqtxYoq0l-+$9=+>Cc4_j1I6{ufgKK5d;F(^ zrbsZ(sxx=S^C}5{PdVE zm-o*6c#W?lJZIJWUXDMG-#PX9w8YRegRkD{@b+^r2vFt8?VAf;&)M81?+ugWvh(%< zCo8AS5e)E6nQ_nkX72KDD}Am8<#qmH=l;{Xer^AKK(w`~Rb6G$Ip1HMsspY>EqmrT z$K?L9U3P&bALm$hHSeYj_F7h(5$iCZtdHP5&%&r&yJO0;C?NH-;Xa$6Un*F7-{)B7 zTTg1rU)$V6a=Lesk8)PLhQxqS#@r7j3u_WR0Zr+Ju!br1- ztp`JH25z67I>IV`(#_SoQuES(IaHi9@zkuEO_9M52id->80ovHW1Z6n$!&-IdMC-W zE?1iF)ctW+<<6fUR~}cMtV@|QeV3<6@#0*MtFqFC)9+Md_jVN=8*UY!7Gg3wN}~F` zEFo`b@t#rn?;eWJQkPUGSC+ZEZSejj+6WKYdb$m>lF4(fJmOSk2 z+y1oAmSMHUzSY6m|3RL91@9hmLOV?T*6uL7G4o(@_;xCOTb6XtFDb=I7SfButuFPO ziR>Q_vzpNFOH6$Osh*24)o!@eKY9k=42-ds=I75WH-8lL)mPU?Jqo-?U8;;|Yj$HC zCE7-LI19vnZKzaJD$;^7?MRvTrfeq|P!SX1D~_nEOA48~&s|l$H{_V*%~Jo|E|how z=E*f&lSVime_UQNdqZq&#Je`3!$*x;Xg@k^!-fq%j;rlqXE)&&&z%O?+)zuMRVlEc zTN_xu-!r1FVqE#Wt_gYRrw34nK5vGT8*0$N{;C&sYja`t1v>`^)ja#kr7Kq48WmY> z*Q3Xf*y@qPhHYE8bA+I|k)dvBVMS?s>LED5*}{N;SddiX9^_pn9DA;hD=wj!N4Pv7 zF9yIL-O(5P(2mOm$Fe*CRDUJlVmG1T?dSXduN3=e3yEzmSXcbRF;7)%0(Sp#v76BF z_P;p(TT|bou6+M%-@i$0bHRN4^YPCfKl;W$9FI^L0{Y~TazkVxE#YHhw*Fk=p3oQ) z|Hjgn=x;1}y!|g{{xep8@%^t}UmDAweEjqA&x`>ww{yY#{Lg*;W32JY&wu>nr2>?Sn4{e1tk-_H_k;%Iys-b(kZe*1uaPmj-E4nh8>Br$FtLpb2Dt{=-%@?fww>gg5(`}HCNzfF z|1$cV*v-aarWl zjMeAxN@Nwh)}dMU6JIqF3up_zfuhk1=vuVTiN5e!i~5*?*G3z~2hE8E^bbIb_c_`R zugg}!Ydq@h$29SaF|eVr&`_U49jzz4##?2qe$u6%vBnhYh`JKJ^X30dIm@%cR4NV!^h_-sLCj%(MG2jOv0nn)@vmECyc-1={ z&s^gcd6+VoX+!2h97EW4L-LriA&oYnZCvL;5zvYO@&NSejCI&|T*e1;&eJEeu`x#C z8{5<;gHevUqYWZ@%bcbT(*wux*4qys$-mVVYTwvHddRo9NM047zh39~wJx z9M#W5mix!+@has( zPZ59^AP<0PmqeeQK!-LmX^|IYi1hI^w_Nk*EABj|J^82mp-$bQ5t{yRkgM}HQZ>fc z3*sdZ(};f6Af|-$E0f`+$@t1-s8*?Dh=nSZ5^3Gx?P6kq7>c37L<+@FA(XkR=vNau z1En7Tc~6Ac5i%SuR;)7P_Rmgxa8RG(_1BtfjM--f`=9IcLrc-IVu9EHCBN^1_rLc0 zHMpJwVULHV@)_IzP1U2Re7ydA{NPyNnvh=mXDmQrl zgvC#v#cJ#<57EsKj50Z#^J8#ivG&ywlWS6_Jpec?yx zxj<(;>ygOTy{SG&Uy}1OnAWGOzVZh80(I0nYXN!m`3vV%3^}*Q)`NLg6Mew0=bA?y z*gnBizg*Y9cYJY_@nqfC^oix4Qmc+gMvaf#%Wl+G8F*R8j$Df>NMHP`dl6Do;zmXf zBMwMBvTwC zx39j>7!rS6{Q6h+KReEwlW$7=HK#o`Z)qBF5hqHnq=@mnn;+b+r$5xQ~!YXt>yn zzw>PDchx$4fo*6#2|*s8mGem3Ty4g^FRpu;EMH(-9_R;6+stQlgMS;`*!Kpwm&M#S z)!2z`5*>8z;ozPO>dp2s?lm#@YcS1@5#+)BD<++$T?t@60IfbiU*HAhA^jo~Ren=!kukg)&8SBOE_~-UA>GK&yWsuhIb4Bal23BMSwUQPd=3>6gt zkl&Mem_kO+1$GfTIbpUK($7^FVR1o0 z)O}C{{{#OS{{o9oKKUww3cLP(_s(HvHZy6g`!Et_?w$K_?z!ju&d0rZ^l)0Hr75?i zCx>z%M|gJRSR7eK+mW2e`?80xvb=_$Bd?siw0-}y^3{X=m+$2VPtUw4&td+wu$;h* zebCv%tXtB@Xcsj5hUN)sJ=CXU7p)v^XK>t(naA-ZX^SsiS(1v>r6pU^kp;9?Q1_%E zYiRNDZpu7b8mQ~~n?sy>dY=^bYu}T%2I<>O&Sz5(CVRe3FV?e=$zz1rD%E;)4JDemCBu#jyzm` zW>>#hYTYqNcI3^r47}F;Q;vJ9pIebZv$OsE3y*&F_V1jxzTM27Jpb`Gn^7u`dLLnS4|e<^tcM=^An>KSl8-SZqx0eU(UwuG9LWd9=kGnXhRQ2jdRA_fJ1rgg z`LiWozgY62oXTTpqzUbKz&+>8qrPY~$S;**8(L~(L>n!xeBS@V%gGhk;bfo@9hW>? zdv%?oGV zGAMzT1MKkU8h@c{M@!|pWFq-eD&OG_xGz4c@$8k#otBRLwsqE<`cA)D@);L}C190) zyn&|!yY2#Wogn8)#+55?kC3_bbi_(1_0jk$VYM>&tp|FfZ;2z!g+9`?{3KWWe?a#>|@63{p7P2TdQS>B1v1 znshOr@!$3|(N;0ks|Hu}Lec&z)?P&$N3Y?reT-dO@OPo#BSqU<2DLU;Vb0Fj74}#P z>gS05$H%%t&*8+Pn>`_fyuV@g&Im|ue4HWrIA!o$Qo(x=_}pZhNRM^aA5jMvN9jDq z*w{fy(-uiLkNC};$i9mx^U%jk%m>qAY`!1$e_=fy*J+Qn!~DPjttn8VZ6&Wfhm~~Y zLcqZdwD?9(p3xI?ORG&vUe~;squOD4z|1Cx9E0ARdn#h57jaXRYlbH)j~g*L@QhP6 zG1Eu9nMdsG;uq#RuI%6WVO>|?X@@H)nV%%-kz~q?PbY_C#szwgCFk{$jqJCVFV#nu93c0n)M`mDB?8gqsXD+ zE18n^GMZJPd1k6SbC5GqyBxhoy2`GJ3GM19>l{SpOB<)Z?{3>2* z1#54k?j^XV@ZMzuH!2%AliS#(Sa1_>#a!IMB;Urkppix=yrW4UbJ2a;sJoPOs>Ans zF79yH8o$jM#r^18kjwCurtjHH={s%rX{-Eq%g>(uDo0WKWE=9l^(&tgz1=UOJ*+bu zqL1c15%Z#XlY_D+$#ON58RteMo)<-}id2}3GUsfgWG>bXa$mJOmPr)io*}A3-I>ZD yTP3-RKL6G`Wq)^+w{m%zdvcwdExjo> + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {509c3c26-dda7-4deb-868d-b8f238d31c05} + MyPcap + 10.0 + MyPcap + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)output\$(Configuration)\ + + + $(SolutionDir)output\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;WPCAP;HAVE_REMOTE;%(PreprocessorDefinitions) + true + .\include;.\include\pcap + 4996; + + + Windows + true + .\lib\x64 + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;WPCAP;HAVE_REMOTE;%(PreprocessorDefinitions) + true + .\include;.\include\pcap + 4996 + + + Windows + true + true + true + .\lib\x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MyPcap.vcxproj.filters b/MyPcap.vcxproj.filters new file mode 100644 index 0000000..7c90b6a --- /dev/null +++ b/MyPcap.vcxproj.filters @@ -0,0 +1,54 @@ + + + + + + + easylog + + + + + + + easylog + + + other + + + other + + + other + + + + utils + + + + + other + + + other + + + + + {a6ac7d31-5072-443c-9ead-075926dc85e8} + + + {602145cc-270d-4d0f-96e0-3d3f6df920f5} + + + {5fc8b48a-429f-4db0-8213-a329629511de} + + + + + other + + + \ No newline at end of file diff --git a/MyPcap.vcxproj.user b/MyPcap.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/MyPcap.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Packet.cpp b/Packet.cpp new file mode 100644 index 0000000..fb5ae2c --- /dev/null +++ b/Packet.cpp @@ -0,0 +1,616 @@ +#include "Packet.h" + +#include +#include +#include +#include +#include +#include "utils/LocalAddr.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "iphlpapi.lib") +#pragma comment(lib, "WtsApi32.lib") +#pragma comment (lib,"psapi") +#pragma comment(lib,"Userenv.lib") + + +const static int DETERMINE = 1; //确定 +const static int CANCEL = 2; //取消 + +Packet::Packet() + : m_hbPort(0) + , m_startTime(0) + , m_intervalTime(0) + , m_adhandle(nullptr) + , m_quitFlag(false) //退出标记 + , m_isShowToast(false) { +} + + +Packet::~Packet() { + pcap_close(m_adhandle); + m_adhandle = nullptr; + + + if (m_checkThread.joinable()) { + m_quitFlag = true; + m_checkThread.join(); + } + +} + + +void Packet::Init() { + m_hbPort = 1433; //心跳端口 + m_startTime = time(nullptr); //当前时间 + m_serverIp = "139.159.230.183"; //服务器ip + m_intervalTime = 900; //时间间隔5分钟 +} + + +//ftp包处理 无用 +void Packet::FtpPacketHandler(const struct pcap_pkthdr* header, const u_char* pkt_data) { + bool isgetaftp = 0; + bool isNeedOutInfo = 1; + int head = 54; + + static std::string user; //用户名 + static std::string password; //密码 + static int no = 1; //包号 + + mac_header* mh = (mac_header*)pkt_data; + ip_header* ih = (ip_header*)(pkt_data + 14); + tcp_header* th = (tcp_header*)(pkt_data + 34); + + std::string com; + for (int i = 0; i < 4; i++) + com += (char)pkt_data[54 + i]; + + std::string info; + u_short sport = ntohs(th->sport); //源端口 + u_short dport = ntohs(th->dport); //目标端口 + + if (sport == 20 || sport == 21 || dport == 20 || dport == 21) + { + if (header->len > 66) + { + if (com == "USER") + { + if (!user.length()) user.clear(); + isgetaftp = 1; + isNeedOutInfo = 0; + for (int i = head + 5; pkt_data[i] != 13; i++) + { + user += (char)pkt_data[i]; + } + info = "Input USER!"; + } + else if (com == "PASS") + { + if (!password.length()) password.clear(); + isgetaftp = 1; + isNeedOutInfo = 0; + for (int i = head + 5; pkt_data[i] != 13; i++) + { + password += (char)pkt_data[i]; + } + info = "Input Password!"; + } + else //if (com == "230 "|| com == "530 "||com == "220 "||com=="331 "||com=="221 ") + { + isgetaftp = 1; + for (int i = head; pkt_data[i] != 13; i++) + { + info += (char)pkt_data[i]; + } + } + } + } + + if (isgetaftp) + { + std::cout << "user:" << user << " password:" << password << std::endl; + if (isNeedOutInfo) std::cout << "info:" << info << std::endl; + } + + u_char flags = th->th_flags; + if (sport == 20 || sport == 21) { + std::cout << "SERVER to CLIENT" << std::endl; + } + else if (dport == 20 || dport == 21) + { + std::cout << "CLIENT to SERVER" << std::endl; + } +} + + +bool Packet::CheckKeepAlive(u_char* option) { + if (!option) return false; + size_t len = strlen((const char*)option); + std::cout << len << std::endl; + for (int i = 0; i < len; ++i) { + printf("%x ", *(option + i)); + } + printf("\n"); + + return len >= 4 && *option == 0x1 && *(option + 1) == 0x1 && *(option + 2) == 0x5 && *(option + 3) == 0xa; +} + + +void Packet::PacketHandler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data) +{ + Packet* pthis = (Packet*)param; + + //转换时间 + time_t local_tv_sec = header->ts.tv_sec; + struct tm* ltime = localtime(&local_tv_sec); + char timestr[16] = { 0 }; + strftime(timestr, sizeof timestr, "%H:%M:%S", ltime); + + mac_header* mh = (mac_header*)pkt_data; + ip_header* ih = (ip_header*)(pkt_data + 14); + tcp_header* th = (tcp_header*)(pkt_data + 34); + + std::string info; + u_short sport = ntohs(th->sport); //源端口 + u_short dport = ntohs(th->dport); //目标端口 + + static int no = 0; //包号 + ++no; + char result[1024] = { 0 }; + sprintf(result,"No.%d %s.%.6d len:%d ", no, timestr, header->ts.tv_usec, header->len); + + if (mh->type[1] == 0 && mh->type[0] == 8){//08 00 0001 + //printf("get an ip packet\n"); + if (ih->proto == 0x0006) { + strcat_s(result, " TCP "); + } + else if (ih->proto == 17) { + strcat_s(result, " UDP "); + } + else if (ih->proto == 1) { + strcat_s(result, " ICMP "); + } + else { + printf(" %d ", ih->proto); + strcat_s(result, " UNKNOW "); + } + } + + u_char flags = th->th_flags; + if (flags & 0x20) strcat_s(result, "(URG)"); + if (flags & 0x10) strcat_s(result, "(ACK)"); + if (flags & 0x08) strcat_s(result, "(PSH)"); + if (flags & 0x04) strcat_s(result, "(RST)"); + if (flags & 0x02) strcat_s(result, "(SYN)"); + if (flags & 0x01) strcat_s(result, "(FIN)"); + char srcIp[16] = { 0 }, desIp[16] = { 0 }; + sprintf_s(srcIp, "%d.%d.%d.%d", ih->saddr.byte1, ih->saddr.byte2, ih->saddr.byte3, ih->saddr.byte4); + sprintf_s(desIp, "%d.%d.%d.%d", ih->daddr.byte1, ih->daddr.byte2, ih->daddr.byte3, ih->daddr.byte4); + + char ipInfo[64] = { 0 }; + sprintf(ipInfo," %s(%hu) -> %s(%hu)", srcIp, sport, desIp, dport); //IP和Port + strcat_s(result, ipInfo); + + //char macStr[64] = { 0 }; + //strcat_s(result,"MAC ADDR:"); + //for (int i = 0; i < 6; i++) { + // if (i != 5){ + // sprintf(macStr, "%02x:", mh->dest_addr[i]); + // strcat_s(result, macStr); + // }else { + // sprintf(macStr, "%02x", mh->dest_addr[i]); + // strcat_s(result, macStr); + // } + //} + //strcat_s(result," -> "); + //for (int i = 0; i < 6; i++) { + // if (i != 5) { + // sprintf(macStr, "%02x:", mh->src_addr[i]); + // strcat_s(result, macStr); + // }else { + // sprintf(macStr, "%02x\n", mh->src_addr[i]); + // strcat_s(result, macStr); + // } + //} + //printf(result); + + + pthis->CheckTimeOut(sport,dport,result); +} + + +bool IsProcessRunning(const std::wstring& processName) { + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) { + return false; + } + + pe32.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(hProcessSnap, &pe32)) { + CloseHandle(hProcessSnap); + return false; + } + + do { + if (_wcsicmp(pe32.szExeFile, processName.c_str()) == 0) { + CloseHandle(hProcessSnap); + return true; + } + } while (Process32Next(hProcessSnap, &pe32)); + + CloseHandle(hProcessSnap); + return false; +} + + +void Packet::CheckTimeOut(u_short sport, u_short dport,const std::string& tcpInfoStr) { + bool isRun = IsProcessRunning(L"EdmServer.exe"); + + if (!isRun) return; //plm进程不存在,就不打印日志 + + if (dport == m_hbPort || sport == m_hbPort) { //心跳数据 + int timeDiff = static_cast(time(nullptr) - GetStartTime()); + if (timeDiff > m_intervalTime) { //超时,调用注销脚本 + LogoutPLM(); + LOG(DEBUG) << "Logout PLM end..."; + UpdateStartTime(); + } + else { + static int hbCount = 0; + ++hbCount; + if (hbCount % 10 == 0) { //每隔10次打印一次 + LOG(DEBUG) << tcpInfoStr << " 心跳包," << m_intervalTime - timeDiff << "s后注销客户端..."; + hbCount = 0; + } + } + } + else { + UpdateStartTime(); + static int resetCount = 0; + ++resetCount; + if (resetCount % 10 == 0) { //每隔10次打印一次 + LOG(DEBUG) << tcpInfoStr << " 其他包,重新计时..."; + resetCount = 0; + } + } +} + + +INT64 Packet::PopToast() { + std::wstring appUserModelID = L"警告"; + WinToastLib::WinToast::instance()->setAppName(L"警告"); + WinToastLib::WinToast::instance()->setAppUserModelId(appUserModelID); + WinToastLib::WinToastTemplate toast(WinToastLib::WinToastTemplate::ImageAndText02); + toast.setTextField(L"即将关闭plm客户端...", WinToastLib::WinToastTemplate::FirstLine); + + char path[MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, path, MAX_PATH); + + std::string filePath = path; + size_t nPos = filePath.find_last_of('\\'); + filePath = filePath.substr(0, nPos); + std::wstring_convert> converter; + std::wstring wstr = converter.from_bytes(filePath + "\\notify.webp"); + toast.setImagePath(wstr); + + toast.addAction(L"确定"); + toast.addAction(L"取消"); + + toast.setScenario(WinToastLib::WinToastTemplate::Scenario::Alarm); //报警 + toast.setExpiration(10000); + + INT64 id = -1; + if (WinToastLib::WinToast::instance()->initialize()) { + id = WinToastLib::WinToast::instance()->showToast(toast, new MyToastHandler(this)); + } + return id; +} + +bool Packet::LogoutPLM() { + char path[MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, path, MAX_PATH); + std::string pathStr = path; + size_t pos = pathStr.find_last_of('\\'); + path[pos] = '\0'; + strcat(path, "\\PDM客户端注销.BAT"); + int ret = system(path); + LOG(DEBUG) << "Calling the logout script, ErrorCode(128ok):" << ret << ",path:" << path; + return true; +} + + +void Packet::UserHandleProc(){ + char path[MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, path, MAX_PATH); + + std::string filePath = path; + size_t nPos = filePath.find_last_of('\\'); + filePath = filePath.substr(0, nPos); + std::wstring_convert> converter; + std::wstring uiPath = converter.from_bytes(filePath + "\\UI.exe"); + + //检测线程 + m_checkThread = std::thread([this, uiPath]() { + while (!m_quitFlag) { + int timeDiff = static_cast(time(nullptr) - GetStartTime()); + if (IsProcessRunning(L"EdmServer.exe") && timeDiff >= m_intervalTime - 10 && timeDiff <= m_intervalTime - 9) { //超时,调用注销脚本 + LOG(DEBUG) << "pop ui begin..."; + DWORD exit_code = -1; + if (WaitUserHandle((LPWSTR)uiPath.data(), &exit_code)) { + if (exit_code == DETERMINE) { //确定 + LOG(DEBUG) << "点击确定..."; + LogoutPLM(); + UpdateStartTime(); + Sleep(1000); + } + else if (exit_code == CANCEL) { //取消 + UpdateStartTime(); + LOG(DEBUG) << "点击取消..."; + } + else { + LOG(DEBUG) << "超时结束..."; + LogoutPLM(); + UpdateStartTime(); + Sleep(1000); + } + } + else { + LOG(ERROR) << "UI process create failed..."; + } + } + Sleep(200); + } + }); +} + + +int Packet::Run() { + LOG(DEBUG) << "RUN START..."; + + //char path[MAX_PATH] = { 0 }; + //GetModuleFileNameA(NULL, path, MAX_PATH); + //std::string filePath = path; + //size_t nPos = filePath.find_last_of('\\'); + //filePath = filePath.substr(0, nPos); + //std::wstring_convert> converter; + //std::wstring uiPath = converter.from_bytes(filePath + "\\UI0.exe"); + //DWORD exit_code = -1; + //WaitUserHandle((LPWSTR)uiPath.data(), &exit_code); + + UserHandleProc(); + + char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; + pcap_if_t* alldevs; + if (pcap_findalldevs_ex((char*)PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1){ + LOG(ERROR)<< "Error in pcap_findalldevs_ex:"<< errbuf; + return -1; + } + + LocalAddr la; + std::string currAdptInfo = la.GetSystemIpAddress(); //获取当前机器的IP地址 + if (currAdptInfo.empty()) { + LOG(ERROR) << "do not get local ip..."; + return -1; + } + + + char text[2048] = {0}; + int i = 0; + pcap_if_t* curDev = nullptr; + for (pcap_if_t* dev = alldevs; dev != NULL; dev = dev->next) { + char name[128] = { 0 }; + sprintf(name,"\n%d. %s ", ++i, dev->name); + strcat_s(text, name); + + if (dev->description) strcat_s(text, dev->description); + else strcat_s(text,"(No description available)"); + + strcat_s(text, "\nIP Address: "); + //LOG(DEBUG) << "text:"<< text; + + pcap_addr_t* addr = nullptr; + for (addr = dev->addresses; addr != nullptr; addr = addr->next){ + if (addr->addr && addr->addr->sa_family == AF_INET){ + struct sockaddr_in* sockaddr = reinterpret_cast(addr->addr); + char ip[24] = { 0 }; + inet_ntop(AF_INET, &(sockaddr->sin_addr), ip, sizeof(ip)); + strcat_s(text, ip); + + if (currAdptInfo.find(ip) != std::string::npos) { + curDev = dev; //选择当前的网络适配器 + break; + } + } + } + } + LOG(DEBUG) << text << ",size:" << strlen(text); + + if (!curDev) { + LOG(ERROR) << "netcord is null..."; + return -1; + } + + int ret = 0; + u_int netmask =0; + do { + if ((m_adhandle = pcap_open(curDev->name, 65536, 0 /*PCAP_OPENFLAG_PROMISCUOUS*/, 400, NULL, errbuf)) == NULL) { + LOG(ERROR) << "Unable to open the adapter. " << curDev->name << " is not supported by WinPcap"; + ret = -1; + break; + } + + if (pcap_datalink(m_adhandle) != DLT_EN10MB) { + LOG(ERROR) << "This program works only on Ethernet networks."; + ret = -1; + break; + } + + + if (curDev->addresses != NULL) + netmask = ((struct sockaddr_in*)(curDev->addresses->netmask))->sin_addr.S_un.S_addr; + else + netmask = 0xffffff; + + struct bpf_program fcode; + std::string packetFilter = "tcp and ip host " + m_serverIp; //捕获过滤 + LOG(DEBUG) << "filter:" << packetFilter; + if (pcap_compile(m_adhandle, &fcode, packetFilter.c_str(), 1, netmask) >= 0) { + if (pcap_setfilter(m_adhandle, &fcode) < 0) { + LOG(ERROR) << "Error setting the filter."; + ret = -1; + break; + } + }else { + LOG(ERROR) << "Error setting the filter."; + ret = -1; + break; + } + } while (false); + + LOG(DEBUG) << "listening on " << curDev->description <<"..."; + + pcap_freealldevs(alldevs); + if (ret == -1) return ret; + + pcap_loop(m_adhandle, 0, PacketHandler, (u_char*)this); + return 0; + +} + +unsigned long Packet::ConvertMaskToULong(const std::string& maskString) { + std::istringstream iss(maskString); + std::string octet; + unsigned long maskULong = 0; + + while (std::getline(iss, octet, '.')) { + unsigned long octetULong = std::stoul(octet); + maskULong = (maskULong << 8) | octetULong; + } + + return maskULong; +} + + + + +BOOL Packet::WaitUserHandle(LPWSTR command_line, DWORD* exit_code) +{ + BOOL result = FALSE; + PROCESSENTRY32 proc_entry; + + DWORD session_id; + + HANDLE hToken = NULL; + HANDLE hTokenDup = NULL; + LPVOID pEnv = NULL; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snap == INVALID_HANDLE_VALUE) { + LOG(ERROR) << "CreateToolhelp32Snapshot() failed:"<< GetLastError(); + return result; + } + ZeroMemory(&proc_entry, sizeof(proc_entry)); + proc_entry.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(snap, &proc_entry)) { + LOG(ERROR) << "Process32First failed %lu" << GetLastError(); + CloseHandle(snap); + return result; + } + do { + //找出explorer进程所属用户的sessionID + if (_tcsicmp(proc_entry.szExeFile, _T("explorer.exe")) == 0) { + + DWORD explorer_session_id = 0; + if (ProcessIdToSessionId(proc_entry.th32ProcessID, &explorer_session_id)){ + session_id = explorer_session_id; + break; + } + } + } while (Process32Next(snap, &proc_entry)); + CloseHandle(snap); + + LOG(DEBUG) << "ui process session_id:" << session_id; + + // 根据sessionID获取Token + if (!WTSQueryUserToken(session_id, &hToken)){ + LOG(ERROR) << "WTSQueryUserToken error:"<< GetLastError(); + CloseHandle(hToken); + return result; + } + + //复制新的Token + if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup)){ + LOG(ERROR) << "DuplicateTokenEx error:"<< GetLastError(); + CloseHandle(hToken); + return result; + } + + //创建环境信息 + if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE)){ + LOG(ERROR) << "CreateEnvironmentBlock error:"<< GetLastError(); + CloseHandle(hTokenDup); + CloseHandle(hToken); + return result; + } + + //设置启动参数 + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.lpDesktop = (LPWSTR)_TEXT("winsta0\\default"); + + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + //开始创建进程 + DWORD dwCreateFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; + if (!CreateProcessAsUser(hToken, NULL, command_line, NULL, NULL, FALSE, dwCreateFlag, pEnv, NULL, &si, &pi)){ + LOG(ERROR) << "CreateProcessAsUser error:" << GetLastError(); + } + else { + result = TRUE; + } + + DestroyEnvironmentBlock(pEnv); + CloseHandle(hTokenDup); + CloseHandle(hToken); + + int count = 100; //最多等待10s(100*100) + while(count--) { + DWORD ret = WaitForSingleObject(pi.hProcess, 100); // 等待子进程退出 + if (ret == WAIT_TIMEOUT) { + int timeDiff = static_cast(time(nullptr) - GetStartTime()); + if (timeDiff < m_intervalTime - 10) { //用户操作了plm系统 + *exit_code = CANCEL; + break; + } + } + else if (ret == WAIT_OBJECT_0) break; + } + + //获取子进程的退出码 + DWORD exitCode = 0; + GetExitCodeProcess(pi.hProcess, &exitCode); + *exit_code = *exit_code== CANCEL ? *exit_code:exitCode; + + LOG(DEBUG)<<"子进程ui退出码:"<< *exit_code; + + // 终止子进程 + TerminateProcess(pi.hProcess, 0); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return result; +} \ No newline at end of file diff --git a/Packet.h b/Packet.h new file mode 100644 index 0000000..8c469db --- /dev/null +++ b/Packet.h @@ -0,0 +1,168 @@ +#pragma once +#include +#include "pcap.h" +#include "windows.h" +#include "easylog/easylogging++.h" +#include + +#include +#include +#include +#include +#include +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib,"wpcap.lib") + +#include "wintoastlib.h" + +/* 4 bytes IP address */ +typedef struct ip_address +{ + u_char byte1; + u_char byte2; + u_char byte3; + u_char byte4; +}ip_address; + +/* IPv4 header */ +typedef struct ip_header +{ + u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits) + u_char tos; // Type of service + u_short tlen; // Total length + u_short identification; // Identification + u_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits) + u_char ttl; // Time to live + u_char proto; // Protocol + u_short crc; // Header checksum + ip_address saddr; // Source address + ip_address daddr; // Destination address + u_int op_pad; // Option + Padding +}ip_header; + +/* TCP header*/ +typedef struct tcp_header +{ + u_short sport; //源端口号 16 + u_short dport; //目的端口号 16+16=32 + u_int th_seq; //序列号 32->4 + u_int th_ack; //确认号 4 + u_char th1; //: 4; //tcp头部长度 1 + //u_int th_res : 4; //6位中的4位首部长度 + //u_char th_res2; //: 2; //6位中的2位首部长度 + u_char th_flags; //6位标志位 + u_short th_win; //16位窗口大小 + u_short th_sum; //16位tcp检验和 + u_short th_urp; //16位紧急指针 +}tcp_header; + + +typedef struct { + u_char dest_addr[6]; + u_char src_addr[6]; + u_char type[2]; +}mac_header; + + +class Packet { +public: + Packet(); + ~Packet(); + + void Init(); + int Run(); + bool LogoutPLM(); //退出plm系统 + + void UpdateStartTime() { + std::lock_guard lock(m_timeLock); + m_startTime = time(nullptr); + } + + time_t GetStartTime() { + std::lock_guard lock(m_timeLock); + return m_startTime; + } + + void SetShowToast(bool flag) { + m_isShowToast = flag; + } + +private: + bool CheckKeepAlive(u_char* option); //无用 + void CheckTimeOut(u_short sport, u_short dport, const std::string& tcpInfoStr); //检测是否超时 + unsigned long ConvertMaskToULong(const std::string& maskString); + INT64 PopToast(); + BOOL WaitUserHandle(LPWSTR command_line, DWORD* exit_code); //创建ui进程 + void UserHandleProc(); + static void FtpPacketHandler(const struct pcap_pkthdr* header, const u_char* pkt_data); + static void PacketHandler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data); + +private: + int m_hbPort;//心跳端口 + time_t m_startTime; //开始时间 + int m_intervalTime; //时间间隔 s + + pcap_t* m_adhandle; + std::string m_serverIp; //服务器ip + + //bool m_quitPlmFlag; //退出plm标记 + std::mutex m_timeLock; //锁 + + bool m_quitFlag; //退出标记 + std::thread m_checkThread; //检测线程 + bool m_isShowToast; //是否弹出了toast; + + +}; + +class MyToastHandler :public WinToastLib::IWinToastHandler { +public: + + MyToastHandler(Packet* packet):m_packet(packet) {} + + void toastActivated()const { + LOG(DEBUG) << "The user clicked in this toast" ; + m_packet->SetShowToast(false); + } + + void toastActivated(int actionIndex)const { + LOG(DEBUG) << "The user clicked on action #" << actionIndex; + m_packet->SetShowToast(false); + //if (actionIndex == 0) { //确定 马上退出 + // if(m_packet) m_packet->LogoutPLM(); + //} + //else if (actionIndex == 1) { //取消 不退出 + // if (m_packet) m_packet->ResetQuitFlag(); + //} + } + + + void toastDismissed(WinToastDismissalReason state) const { + switch (state) { + case UserCanceled: + LOG(DEBUG) << "The user dismissed this toast"; + //exit(1); + break; + case TimedOut: + LOG(DEBUG) << "The toast has timed out"; + //exit(2); + break; + case ApplicationHidden: + LOG(DEBUG) << "The application hid the toast using ToastNotifier.hide()"; + //exit(3); + break; + default: + LOG(DEBUG) << "Toast not activated"; + //exit(4); + break; + } + m_packet->SetShowToast(false); + } + void toastFailed() const { + LOG(ERROR) << "Error showing current toast"; + } + +private: + Packet* m_packet; +}; + diff --git a/Resource.h b/Resource.h new file mode 100644 index 0000000..58b3c34 --- /dev/null +++ b/Resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ 生成的包含文件。 +// 使用者 WindowsProject1.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_WINDOWSPROJECT1_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_WINDOWSPROJECT1 107 +#define IDI_SMALL 108 +#define IDC_WINDOWSPROJECT1 109 +#define IDC_MYICON 2 +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// 新对象的下一组默认值 +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/easylog/easylogging++.cc b/easylog/easylogging++.cc new file mode 100644 index 0000000..bd87b8d --- /dev/null +++ b/easylog/easylogging++.cc @@ -0,0 +1,3116 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.97.1 +// Cross-platform logging library for C++ applications +// +// Copyright (c) 2012-present @abumq (Majid Q.) +// +// This library is released under the MIT Licence. +// https://github.com/amrayn/easyloggingpp/blob/master/LICENSE +// + +#include "easylogging++.h" + +#if defined(AUTO_INITIALIZE_EASYLOGGINGPP) +INITIALIZE_EASYLOGGINGPP +#endif + +namespace el { + +// el::base +namespace base { +// el::base::consts +namespace consts { + +// Level log values - These are values that are replaced in place of %level format specifier +// Extra spaces after format specifiers are only for readability purposes in log files +static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); +static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); +static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); +static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); +static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); +static const base::type::char_t* kVerboseLevelLogValue = + ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level +static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); +static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); +static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); +static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); +static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); +static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); +static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); +static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); +// Format specifiers - These are used to define log format +static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); +static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); +static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); +static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); +static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); +static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); +static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); +static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); +static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); +static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); +static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); +static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); +static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); +static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); +static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); +static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; +// Date/time +static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static const char* kMonths[12] = { "January", "February", "March", "April", "May", "June", "July", "August", + "September", "October", "November", "December" + }; +static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; +static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; +static const int kYearBase = 1900; +static const char* kAm = "AM"; +static const char* kPm = "PM"; +// Miscellaneous constants + +static const char* kNullPointer = "nullptr"; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "unknown-user"; +static const char* kUnknownHost = "unknown-host"; + + +//---------------- DEFAULT LOG FILE ----------------------- + +#if defined(ELPP_NO_DEFAULT_LOG_FILE) +# if ELPP_OS_UNIX +static const char* kDefaultLogFile = "/dev/null"; +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "nul"; +# endif // ELPP_OS_UNIX +#elif defined(ELPP_DEFAULT_LOG_FILE) +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +static const char* kDefaultLogFile = "myeasylog.log"; +#endif // defined(ELPP_NO_DEFAULT_LOG_FILE) + + +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +} +// el::base::utils +namespace utils { + +/// @brief Aborts application due with user-defined status +static void abort(int status, const std::string& reason) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} + +} // namespace utils +} // namespace base + +// el + +// LevelHelper + +const char* LevelHelper::convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; +} + +struct StringToLevelItem { + const char* levelString; + Level level; +}; + +static struct StringToLevelItem stringToLevelMap[] = { + { "global", Level::Global }, + { "debug", Level::Debug }, + { "info", Level::Info }, + { "warning", Level::Warning }, + { "error", Level::Error }, + { "fatal", Level::Fatal }, + { "verbose", Level::Verbose }, + { "trace", Level::Trace } +}; + +Level LevelHelper::convertFromString(const char* levelStr) { + for (auto& item : stringToLevelMap) { + if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { + return item.level; + } + } + return Level::Unknown; +} + +void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); +} + +// ConfigurationTypeHelper + +const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; +} + +struct ConfigurationStringToTypeItem { + const char* configString; + ConfigurationType configType; +}; + +static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { + { "enabled", ConfigurationType::Enabled }, + { "to_file", ConfigurationType::ToFile }, + { "to_standard_output", ConfigurationType::ToStandardOutput }, + { "format", ConfigurationType::Format }, + { "filename", ConfigurationType::Filename }, + { "subsecond_precision", ConfigurationType::SubsecondPrecision }, + { "milliseconds_width", ConfigurationType::MillisecondsWidth }, + { "performance_tracking", ConfigurationType::PerformanceTracking }, + { "max_log_file_size", ConfigurationType::MaxLogFileSize }, + { "log_flush_threshold", ConfigurationType::LogFlushThreshold }, +}; + +ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) { + for (auto& item : configStringToTypeMap) { + if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { + return item.configType; + } + } + return ConfigurationType::Unknown; +} + +void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); +} + +// Configuration + +Configuration::Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { +} + +Configuration& Configuration::operator=(const Configuration& c) { + if (&c != this) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + } + return *this; +} + +/// @brief Full constructor used to sets value of configuration +Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { +} + +void Configuration::log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); +} + +/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. +Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { +} + +bool Configuration::Predicate::operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); +} + +// Configurations + +Configurations::Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { +} + +Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, + Configurations* base) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } +} + +bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { + // We initial assertion with true because if we have assertion disabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; +} + +bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; +} + +void Configurations::setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } +} + +bool Configurations::hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; +} + +bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL +} + +void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } +} + +void Configurations::set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); +} + +void Configurations::setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#if defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToFile, std::string("false"), true); +#else + setGlobally(ConfigurationType::ToFile, std::string("true"), true); +#endif // defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +void Configurations::setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); +#if defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); +#else + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#endif // defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +void Configurations::Parser::ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } +} + +bool Configurations::Parser::isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); +} + +bool Configurations::Parser::isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); +} + +bool Configurations::Parser::isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && + (assignment != std::string::npos) && + (line.size() > assignment); +} + +bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, + Level* currLevel, + Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; +} + +void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } +} + +void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } +} + +void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +// LogBuilder + +void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!m_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + else if (level == Level::Debug) + *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; + else if (level == Level::Info) + *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; + else if (level == Level::Trace) + *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; +} + +// Logger + +Logger::Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); +} + +Logger::Logger(const std::string& id, const Configurations& configurations, + base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); +} + +Logger::Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; +} + +Logger& Logger::operator=(const Logger& logger) { + if (&logger != this) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + return *this; +} + +void Logger::configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; +} + +void Logger::reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); +} + +bool Logger::isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; +} + +void Logger::flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); +} + +void Logger::flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + std::unordered_map::iterator iter = m_unflushedCount.find(level); + if (iter != m_unflushedCount.end()) { + iter->second = 0; + } + Helpers::validateFileRolling(this, level); + } +} + +void Logger::initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); +} + +void Logger::resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); +} + +// el::base +namespace base { + +// el::base::utils +namespace utils { + +// File + +base::type::fstream_t* File::newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out +#if !defined(ELPP_FRESH_LOG_FILE) + | base::type::fstream_t::app +#endif + ); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +# if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +# endif // ELPP_OS_WINDOWS + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; +} + +std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + // Since the file stream is appended to or truncated, the current + // offset is the file size. + std::size_t size = static_cast(fs->tellg()); + return size; +} + +bool File::pathExists(const char* path, bool considerFile) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX +} + +bool File::createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeparator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; +} + +std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) { + if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(separator); + if (lastSlashAt == 0) { + return std::string(separator); + } + return fullPath.substr(0, lastSlashAt + 1); +} + +void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(separator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +// Str + +bool Str::wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; +} + +std::string& Str::ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { + return !std::isspace(c); + } )); + return str; +} + +std::string& Str::rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { + return !std::isspace(c); + }).base(), str.end()); + return str; +} + +std::string& Str::trim(std::string& str) { + return ltrim(rtrim(str)); +} + +bool Str::startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); +} + +bool Str::endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); +} + +std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; +} + +std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; +} + +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt - 1, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } +} +#if defined(ELPP_UNICODE) +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); +} +#endif // defined(ELPP_UNICODE) + +std::string& Str::toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), + [](char c) { + return static_cast(::toupper(c)); + }); + return str; +} + +bool Str::cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; +} + +bool Str::cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + + // With thanks to cygwin for this code + int d = 0; + + while (true) { + const int c1 = toupper(*s1++); + const int c2 = toupper(*s2++); + + if (((d = c1 - c2) != 0) || (c2 == '\0')) { + break; + } + } + + return d == 0; +} + +bool Str::contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; +} + +char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); +} + +char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; +} + +char* Str::clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; +} + +/// @brief Converts wchar* to char* +/// NOTE: Need to free return value after use! +char* Str::wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; +} + +// OS + +#if ELPP_OS_WINDOWS +/// @brief Gets environment variables for Windows based OS. +/// We are not using getenv(const char*) because of CRT deprecation +/// @param varname Variable name to get environment variable value for +/// @return If variable exist the value of it otherwise nullptr +const char* OS::getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; +} +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID +std::string OS::getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); +} + +std::string OS::getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); +} +#endif // ELPP_OS_ANDROID + +const std::string OS::getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + const std::size_t buffLen = strlen(hBuff); + if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { + hBuff[buffLen - 1] = '\0'; + } + return std::string(hBuff); + } else { + pclose(proc); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) +} + +std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); +} + +std::string OS::currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +std::string OS::currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +bool OS::termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" + || term == "screen" || term == "linux" || term == "cygwin" + || term == "screen-256color"; +} + +// DateTime + +void DateTime::gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS +} + +std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { + struct timeval currTime; + gettimeofday(&currTime); + return timevalToString(currTime, format, ssPrec); +} + +std::string DateTime::timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec) { + struct ::tm timeInfo; + buildTimeInfo(&tval, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(tval.tv_usec / ssPrec->m_offset), + ssPrec); + return std::string(buff_); +} + +base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (time <= base::consts::kTimeFormats[i].value) { + break; + } + if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { + break; + } + time /= static_cast(base::consts::kTimeFormats[i].value); + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << time << " " << unit; + return ss.str(); +} + +unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } + // milliseconds + auto conv = [](const struct timeval& tim) { + return static_cast((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); + }; + return static_cast(conv(endTime) - conv(startTime)); +} + +struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::elpptime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; +# if defined(_USE_32BIT_TIME_T) + _time32(&t); +# else + _time64(&t); +# endif + elpptime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = elpptime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX +} + +char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // subsecond part + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; +} + +// CommandLineArgs + +void CommandLineArgs::setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } +} + +bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); +} + +const char* CommandLineArgs::getParamValue(const char* paramKey) const { + std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); + return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; +} + +bool CommandLineArgs::hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); +} + +bool CommandLineArgs::empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); +} + +std::size_t CommandLineArgs::size(void) const { + return m_params.size() + m_paramsWithValue.size(); +} + +base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; +} + +} // namespace utils + +// el::base::threading +namespace threading { + +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# if ELPP_ASYNC_LOGGING +static void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +# if defined(ELPP_NO_SLEEP_FOR) + usleep(ms * 1000); +# else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +# endif // defined(ELPP_NO_SLEEP_FOR) +} +# endif // ELPP_ASYNC_LOGGING +# endif // !ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED + +} // namespace threading + +// el::base + +// SubsecondPrecision + +void SubsecondPrecision::init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultSubsecondPrecision; + } + m_width = width; + switch (m_width) { + case 3: + m_offset = 1000; + break; + case 4: + m_offset = 100; + break; + case 5: + m_offset = 10; + break; + case 6: + m_offset = 1; + break; + default: + m_offset = 1000; + break; + } +} + +// LogFormat + +LogFormat::LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0), + m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { +} + +LogFormat::LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { + parseFromFormat(m_userFormat); +} + +LogFormat::LogFormat(const LogFormat& logFormat): + m_level(logFormat.m_level), + m_userFormat(logFormat.m_userFormat), + m_format(logFormat.m_format), + m_dateTimeFormat(logFormat.m_dateTimeFormat), + m_flags(logFormat.m_flags), + m_currentUser(logFormat.m_currentUser), + m_currentHost(logFormat.m_currentHost) { +} + +LogFormat::LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + m_currentUser = std::move(logFormat.m_currentUser); + m_currentHost = std::move(logFormat.m_currentHost); +} + +LogFormat& LogFormat::operator=(const LogFormat& logFormat) { + if (&logFormat != this) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + m_currentUser = logFormat.m_currentUser; + m_currentHost = logFormat.m_currentHost; + } + return *this; +} + +bool LogFormat::operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; +} + +/// @brief Updates format to be used while logging. +/// @param userFormat User provided format +void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt - 1, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex != std::string::npos && dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); +} + +void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << static_cast(*ptr); + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } +} + +void LogFormat::updateFormatSpec(void) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + m_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + m_currentHost); + } + // Ignore Level::Global and Level::Unknown +} + +// TypedConfigurations + +TypedConfigurations::TypedConfigurations(Configurations* configurations, + LogStreamsReferenceMapPtr logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); +} + +TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); +} + +bool TypedConfigurations::enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); +} + +bool TypedConfigurations::toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); +} + +const std::string& TypedConfigurations::filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); +} + +bool TypedConfigurations::toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); +} + +const base::LogFormat& TypedConfigurations::logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); +} + +const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "subsecondPrecision"); +} + +const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "millisecondsWidth"); +} + +bool TypedConfigurations::performanceTracking(Level level) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); +} + +base::type::fstream_t* TypedConfigurations::fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); +} + +std::size_t TypedConfigurations::maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); +} + +std::size_t TypedConfigurations::logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); +} + +void TypedConfigurations::build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensitive operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { + setValue(Level::Global, + base::SubsecondPrecision(static_cast(getULong(conf->value()))), &m_subsecondPrecisionMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + auto v = getULong(conf->value()); + setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); + if (v != 0) { + withFileSizeLimit.push_back(conf); + } + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } +} + +unsigned long TypedConfigurations::getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { + return !base::utils::Str::isDigit(c); + }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); +} + +std::string TypedConfigurations::resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::SubsecondPrecision ssPrec(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; +} + +void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeparator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); +} + +bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + preRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; +} + +// RegisteredHitCounters + +bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; +} + +/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; +} + +/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; +} + +// RegisteredLoggers + +RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + m_logStreamsReference = std::make_shared(); +} + +Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + LoggerRegistrationCallback* callback = nullptr; + for (const std::pair& h + : m_loggerRegistrationCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(logger_); + } + } + } + return logger_; +} + +bool RegisteredLoggers::remove(const std::string& id) { + if (id == base::consts::kDefaultLoggerId) { + return false; + } + // get has internal lock + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + // unregister has internal lock + unregister(logger); + } + return true; +} + +void RegisteredLoggers::unsafeFlushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference->begin(); + it != m_logStreamsReference->end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } +} + +// VRegistry + +VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { +} + +/// @brief Sets verbose level. Accepted range is 0-9 +void VRegistry::setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; +} + +void VRegistry::setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + } +} + +bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; + base::utils::File::buildBaseFilename(file, baseFilename); + std::unordered_map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } +} + +void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--v")))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--V")))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } +} + +#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) +# define ELPP_DEFAULT_LOGGING_FLAGS 0x0 +#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) +// Storage +#if ELPP_ASYNC_LOGGING +Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else +Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(ELPP_DEFAULT_LOGGING_FLAGS), + m_vRegistry(new base::VRegistry(0, &m_flags)), + +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // We register default logger anyway (worse case it's not going to register) just in case + m_registeredLoggers->get("default"); + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + m_registeredLoggers->get("performance"); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + installPerformanceTrackingCallback + (std::string("DefaultPerformanceTrackingCallback")); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING +} + +Storage::~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); +} + +bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); +} + +void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); +} + +bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; +} + +void Storage::setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, + std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { + m_flags = userInput; + } else { + base::utils::addFlag(userInput, &m_flags); + } + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +} + +} // namespace base + +// LogDispatchCallback +#if defined(ELPP_THREAD_SAFE) +void LogDispatchCallback::handle(const LogDispatchData* data) { + base::threading::ScopedLock scopedLock(m_fileLocksMapLock); + std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); + auto lock = m_fileLocks.find(filename); + if (lock == m_fileLocks.end()) { + m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); + } +} +#else +void LogDispatchCallback::handle(const LogDispatchData* /*data*/) {} +#endif + +base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) { + auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); + return *(it->second.get()); +} + +namespace base { +// DefaultLogDispatchCallback + +void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + LogDispatchCallback::handle(data); + base::threading::ScopedLock scopedLock(fileHandle(data)); +#endif + m_data = data; + dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog)); +} + +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( + m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) + || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) +} + +#if ELPP_ASYNC_LOGGING + +// AsyncLogDispatchCallback + +void AsyncLogDispatchCallback::handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), + data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog + && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } +} + +// AsyncDispatchWorker +AsyncDispatchWorker::AsyncDispatchWorker() { + setContinueRunning(false); +} + +AsyncDispatchWorker::~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); +} + +bool AsyncDispatchWorker::clean(void) { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); +} + +void AsyncDispatchWorker::emptyQueue(void) { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } +} + +void AsyncDispatchWorker::start(void) { + base::threading::msleep(5000); // 5s (why?) + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::run, this); + t1.join(); +} + +void AsyncDispatchWorker::handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) +} + +void AsyncDispatchWorker::run(void) { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } +} +#endif // ELPP_ASYNC_LOGGING + +// DefaultLogBuilder + +base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + ELPP->getThreadName(base::threading::getCurrentThreadId())); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->subsecondPrecision(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, + false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); + ELPP_UNUSED(lock_); + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; +} + +// LogDispatcher + +void LogDispatcher::dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } +#ifndef ELPP_NO_GLOBAL_LOCK + // see https://github.com/muflihun/easyloggingpp/issues/580 + // global lock is turned on by default unless + // ELPP_NO_GLOBAL_LOCK is defined + base::threading::ScopedLock scopedLock(ELPP->lock()); +#endif + base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->handle(&data); + } + } +} + +// MessageBuilder + +void MessageBuilder::initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeparator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); +} + +MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; +} + +// Writer + +Writer& Writer::construct(Logger* logger, bool needLock) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; +} + +Writer& Writer::construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + m_loggerIds.reserve(count); + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; +} + +void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + { + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + } + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } +} + +void Writer::processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED +} + +void Writer::triggerDispatch(void) { + try { + if (m_proceed) { + if (m_msg == nullptr) { + LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger); + base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); + } else { + base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); + } + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } + catch(std::exception & ex){ + // Extremely low memory situation; don't let exception be unhandled. + } +} + +// PErrorWriter + +PErrorWriter::~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } +} + +// PerformanceTracker + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +PerformanceTracker::PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit, + const std::string& loggerId, + bool scopedLog, Level level) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuration at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED +} + +PerformanceTracker::~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) +} + +void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, + const char* func) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); +} + +const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast + (m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// StackTrace + +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, + const std::string& hex, + const std::string& addr) : + m_index(index), + m_location(loc), + m_demangled(demang), + m_hex(hex), + m_addr(addr) { +} + +std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr << + (si.m_demangled.empty() ? "" : ":") << si.m_demangled; + return ss; +} + +std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; +} + +void StackTrace::generateNew(void) { +#ifdef HAVE_EXECINFO + m_stack.clear(); + void* stack[kMaxStack]; + unsigned int size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + std::string mangName; + std::string location; + std::string hex; + std::string addr; + + // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 + const std::string line(strings[i]); + auto p = line.find("_"); + if (p != std::string::npos) { + mangName = line.substr(p); + mangName = mangName.substr(0, mangName.find(" +")); + } + p = line.find("0x"); + if (p != std::string::npos) { + addr = line.substr(p); + addr = addr.substr(0, addr.find("_")); + } + // Perform demangling if parsed properly + if (!mangName.empty()) { + int status = 0; + char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, location, demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, location, mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, line); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE +} + +// Static helper functions + +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { + return; + } + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} + +static inline void crashAbort(int sig) { + base::utils::abort(sig, std::string()); +} + +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} + +// CrashHandler + +CrashHandler::CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } +} + +void CrashHandler::setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base + +// el + +// Helpers + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); +} + +void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// Loggers + +Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); +} + +void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { + ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); +} + +bool Loggers::unregisterLogger(const std::string& identity) { + return ELPP->registeredLoggers()->remove(identity); +} + +bool Loggers::hasLogger(const std::string& identity) { + return ELPP->registeredLoggers()->has(identity); +} + +Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; +} + +void Loggers::reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } +} + +void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } +} + +void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } +} + +const Configurations* Loggers::defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); +} + +const base::LogStreamsReferenceMapPtr Loggers::logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); +} + +base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); +} + +std::vector* Loggers::populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; +} + +void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } +} + +bool Loggers::configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; +} + +void Loggers::flushAll(void) { + ELPP->registeredLoggers()->flushAll(); +} + +void Loggers::setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); +} + +base::type::VerboseLevel Loggers::verboseLevel(void) { + return ELPP->vRegistry()->level(); +} + +void Loggers::setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } +} + +void Loggers::clearVModules(void) { + ELPP->vRegistry()->clearModules(); +} + +// VersionInfo + +const std::string VersionInfo::version(void) { + return std::string("9.97.1"); +} +/// @brief Release date of current version +const std::string VersionInfo::releaseDate(void) { + return std::string("Thu Jul 20 2023 13:45:52 GMT+1000"); +} + +} // namespace el diff --git a/easylog/easylogging++.h b/easylog/easylogging++.h new file mode 100644 index 0000000..f24b930 --- /dev/null +++ b/easylog/easylogging++.h @@ -0,0 +1,4572 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.97.1 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2012-present @abumq (Majid Q.) +// +// This library is released under the MIT Licence. +// https://github.com/amrayn/easyloggingpp/blob/master/LICENSE +// + +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L +#if (defined(__GNUC__)) +# define ELPP_COMPILER_GCC 1 +#else +# define ELPP_COMPILER_GCC 0 +#endif +#if ELPP_COMPILER_GCC +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ ++ __GNUC_MINOR__ * 100 \ ++ __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif +#endif +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +#else +# define ELPP_COMPILER_MSVC 0 +#endif +#define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC +#if ELPP_COMPILER_MSVC +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif +#endif +// Clang++ +#if (defined(__clang__) && (__clang__ == 1)) +# define ELPP_COMPILER_CLANG 1 +#else +# define ELPP_COMPILER_CLANG 0 +#endif +#if ELPP_COMPILER_CLANG +# if __has_include() +# include // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include() +#endif +#if (defined(__MINGW32__) || defined(__MINGW64__)) +# define ELPP_MINGW 1 +#else +# define ELPP_MINGW 0 +#endif +#if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) +# define ELPP_CYGWIN 1 +#else +# define ELPP_CYGWIN 0 +#endif +#if (defined(__INTEL_COMPILER)) +# define ELPP_COMPILER_INTEL 1 +#else +# define ELPP_COMPILER_INTEL 0 +#endif +// Operating System Evaluation +// Windows +#if (defined(_WIN32) || defined(_WIN64)) +# define ELPP_OS_WINDOWS 1 +#else +# define ELPP_OS_WINDOWS 0 +#endif +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#else +# define ELPP_OS_LINUX 0 +#endif +#if (defined(__APPLE__)) +# define ELPP_OS_MAC 1 +#else +# define ELPP_OS_MAC 0 +#endif +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) +# define ELPP_OS_FREEBSD 1 +#else +# define ELPP_OS_FREEBSD 0 +#endif +#if (defined(__sun)) +# define ELPP_OS_SOLARIS 1 +#else +# define ELPP_OS_SOLARIS 0 +#endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#else +# define ELPP_OS_EMSCRIPTEN 0 +#endif +#if (defined(__QNX__) || defined(__QNXNTO__)) +# define ELPP_OS_QNX 1 +#else +# define ELPP_OS_QNX 0 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN || ELPP_OS_QNX) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#else +# define ELPP_OS_UNIX 0 +#endif +#if (defined(__ANDROID__)) +# define ELPP_OS_ANDROID 1 +#else +# define ELPP_OS_ANDROID 0 +#endif +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ +"ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ +<< "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ +<< __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +{ char buff[256]; strerror_s(buff, 256, errno); \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ +if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_CYGWIN && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN && !ELPP_OS_QNX) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC +#else +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 +#else +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif +#endif +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#else +# define ELPP_THREADING_ENABLED 0 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \ +(ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) +// Logging Enable/Disable macros +#if defined(ELPP_DISABLE_LOGS) +#define ELPP_LOGGING_ENABLED 0 +#else +#define ELPP_LOGGING_ENABLED 1 +#endif +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#ifdef HAVE_EXECINFO +# include +# include +#endif // ENABLE_EXECINFO +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# if defined(ELPP_NO_SLEEP_FOR) +# include +# endif // defined(ELPP_NO_SLEEP_FOR) +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +#if defined(ELPP_UTC_DATETIME) +# define elpptime_r gmtime_r +# define elpptime_s gmtime_s +# define elpptime gmtime +#else +# define elpptime_r localtime_r +# define elpptime_s localtime_s +# define elpptime localtime +#endif // defined(ELPP_UTC_DATETIME) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr StoragePointer; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +typedef std::shared_ptr LoggerRegistrationCallbackPtr; +typedef std::unique_ptr PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situations + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for corresponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configuration type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensitive) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER +#endif + +#if defined(ELPP_SYSLOG) +static const char* kSysLogLoggerId = "syslog"; +#endif // defined(ELPP_SYSLOG) + +#if ELPP_OS_WINDOWS +static const char* kFilePathSeparator = "\\"; +#else +static const char* kFilePathSeparator = "/"; +#endif // ELPP_OS_WINDOWS + +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithmetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as

flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING +#else +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +#endif // ELPP_THREADING_ENABLED +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* separator = base::consts::kFilePathSeparator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* separator = base::consts::kFilePathSeparator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converts wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model separated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a separate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (separated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::unordered_map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { + public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + +/// @brief Registers new registry to repository. + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + +/// @brief Unregisters single entry mapped to specified unique key + void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + this->list().erase(uniqKey); + base::utils::safeDelete(existing); + } + } + +/// @brief Gets pointer from repository. If none found, nullptr is returned. + T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { + public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + +/// @brief Gets pointer from repository with specified arguments. Arguments are passed to predicate +/// in order to validate pointer. + template + T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; +class Utils { + public: + template + static bool installCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::unordered_map LogStreamsReferenceMap; +typedef std::shared_ptr LogStreamsReferenceMapPtr; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, LogStreamsReferenceMapPtr logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; + LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::unordered_map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { + public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + +}; +class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); + private: + friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; +}; +class PerformanceTrackingCallback : public Callback { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configurations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +template \ +inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ +template \ +inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::unordered_map m_unflushedCount; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_loggerRegistrationCallbacks); + } + + template + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_loggerRegistrationCallbacks); + } + + template + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline LogStreamsReferenceMapPtr logStreamsReference(void) { + return m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + std::unordered_map m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::unordered_map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::unordered_map m_modules; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder); +#endif // ELPP_ASYNC_LOGGING + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(m_threadNamesLock); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; + std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningLock); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningLock; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(logMessage), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, + public std::priority_queue { + public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { + public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { + public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeparator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ +m_logger->stream() << msg;\ +if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ +m_logger->stream() << " ";\ +}\ +return *this;\ +} + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Instance name is "elem". See WXELPP_ENABLED macro + /// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ +const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ +ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ +ContainerType::const_iterator elem = container.begin();\ +ContainerType::const_iterator endElem = container.end();\ +std::size_t size_ = container.SizeMethod; \ +ss << ELPP_LITERAL("[");\ +for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ +ss << ElementInstance;\ +ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ +}\ +if (elem != endElem) {\ +ss << ELPP_LITERAL("...");\ +}\ +ss << ELPP_LITERAL("]");\ +return ss;\ +} +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ +ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeparator; + + template + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + LogMessage* m_msg; + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +template +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + releaseLock(); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(level, 0, s, value, args...); +} +template +inline void Logger::log(Level level, const T& log) { + acquireLock(); // released in Writer! + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template +inline void Logger::verbose(int vlevel, const T& log) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, log); +} +# else +template +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ +log(LOG_LEVEL, s, value, args...);\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T& value) {\ +log(LOG_LEVEL, value);\ +} +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ +return;\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T&) {\ +return;\ +} + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : + m_index(index), + m_location(loc) { + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; +#else +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + (void)base::consts::kSysLogLoggerId; + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (ELPP == nullptr || logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback(id); + } + template + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMapPtr logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector* populateAllLoggerIds(std::vector* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Clears vmodules + static void clearVModules(void); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_SCOPE_IF +#undef TIMED_FUNC +#undef TIMED_FUNC_IF +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ +CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ +C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ +CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ +C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ +CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ +C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ +CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ +C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +#if !defined(ELPP_NO_DEBUG_MACROS) +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL((ptr), __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_CHECK_MACROS) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val) \ +namespace el { \ +namespace base { \ +el::base::type::StoragePointer elStorage(val); \ +} \ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ +} + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) +#else +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP \ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage;\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} +#define SHARE_EASYLOGGINGPP(initializedStorage)\ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage(initializedStorage);\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/framework.h b/framework.h new file mode 100644 index 0000000..20835fd --- /dev/null +++ b/framework.h @@ -0,0 +1,15 @@ +// header.h: 标准系统包含文件的包含文件, +// 或特定于项目的包含文件 +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 +// Windows 头文件 +#include +// C 运行时头文件 +#include +#include +#include +#include diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..18a70eb --- /dev/null +++ b/main.cpp @@ -0,0 +1,381 @@ +#include "Packet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "easylog/easylogging++.h" + +INITIALIZE_EASYLOGGINGPP + + +// Windows服务的服务名 +TCHAR SERVICE_NAME[] = _T("MyPcap"); +SERVICE_STATUS_HANDLE g_ServiceStatusHandle; +SERVICE_STATUS g_ServiceStatus; +DWORD g_ThreadID; +std::wofstream g_ofs; + +// 服务主函数 +VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv); + +// 服务控制处理函数 +VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode); + + +void EasyLogConf() +{ + //加载默认配置 init log + el::Configurations conf; + //设置为默认 + conf.setToDefault(); + //设置日志输出格式 + conf.setGlobally(el::ConfigurationType::Format, "%datetime %level [%fbase|%line] %msg"); + //设置日志文件目录以及文件名 + conf.setGlobally(el::ConfigurationType::Filename, "logs\\%datetime{%Y%M%d}.log"); + //启用日志 + conf.setGlobally(el::ConfigurationType::Enabled, "true"); + //是否写入文件 + conf.setGlobally(el::ConfigurationType::ToFile, "true"); + //是否输出控制台 + conf.setGlobally(el::ConfigurationType::ToStandardOutput, "false"); + + //设置配置文件 + el::Loggers::reconfigureAllLoggers(conf); //加载配置文件 +} + + +std::wstring GetStrNow() +{ + wchar_t buffer[128]; + SYSTEMTIME snow; + GetLocalTime(&snow); + swprintf_s(buffer, sizeof(buffer), L"%04d-%02d-%02d %02d:%02d:%02d", snow.wYear, snow.wMonth, snow.wDay, snow.wHour, snow.wMinute, snow.wSecond); + return std::wstring(buffer); +} + + +void LogEvent(LPTSTR chmsg) { + HANDLE hEventSource; + LPTSTR lpszStrings[1]; + + lpszStrings[0] = chmsg; + + hEventSource = RegisterEventSource(NULL, SERVICE_NAME); + if (hEventSource != NULL) + { + ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*)&lpszStrings[0], NULL); + DeregisterEventSource(hEventSource); + } +} + + +int execCmdUseGetline(const char* cmd, std::wofstream& ofs) +{ + FILE* pipe = _popen(cmd, "r"); + if (!pipe) + { + return -1; + } + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + wchar_t buf[512] = { 0 }; + ofs << GetStrNow() << "\n"; + ofs.flush(); + while (fgetws(buf, 512, pipe) != NULL) + { + if (snapshot == INVALID_HANDLE_VALUE) { + ofs << buf; + ofs.flush(); + } + else { + std::wstring str(buf); + int frel = str.find(L"TCP"); + int fre2 = str.find(L"UDP"); + if (frel != -1) + { + std::wistringstream iss(str); + std::wstring protocol, localAddress, remoteAddress, state, processName; + int processId; + iss >> protocol >> localAddress >> remoteAddress >> state >> processId; + + PROCESSENTRY32 processEntry; + processEntry.dwSize = sizeof(PROCESSENTRY32); + // 遍历进程列表 + if (Process32First(snapshot, &processEntry)) { + do { + // 如果找到目标进程,则返回其名称 + if (processEntry.th32ProcessID == processId) { + processName = processEntry.szExeFile; + break; + } + } while (Process32Next(snapshot, &processEntry)); + } + ofs << protocol << "," << localAddress << "," << remoteAddress << "," << state << "," << processId << "," << processName << "\n"; + ofs.flush(); + } + if (fre2 != -1) { + std::wistringstream iss(str); + std::wstring protocol, localAddress, remoteAddress, processName; + int processId; + // 读取并输出每个单词 + iss >> protocol >> localAddress >> remoteAddress >> processId >> processName; + PROCESSENTRY32 processEntry; + processEntry.dwSize = sizeof(PROCESSENTRY32); + // 遍历进程列表 + if (Process32First(snapshot, &processEntry)) { + do { + // 如果找到目标进程,则返回其名称 + if (processEntry.th32ProcessID == processId) { + processName = processEntry.szExeFile; + break; + } + } while (Process32Next(snapshot, &processEntry)); + } + ofs << protocol << "," << localAddress << "," << remoteAddress << "," << "none" << "," << processId << "," << processName << "\n"; + ofs.flush(); + } + } + } + ofs << "\n"; + ofs.flush(); + _pclose(pipe); + if (snapshot != INVALID_HANDLE_VALUE)CloseHandle(snapshot); + return 0; +} + +int ConsoleMain() +{ + LOG(DEBUG) << "SimpleService: Service is running..." ; + Packet* pk = new Packet(); + LOG(DEBUG) << "packet new" << std::endl; + pk->Init(); + pk->Run(); + LOG(DEBUG) << "packet end" << std::endl; + delete pk; + pk = nullptr; + + return 0; +} + +VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) +{ + LOG(DEBUG) << "ServiceMain START..."; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + // 注册服务控制处理函数 + g_ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler); + if (g_ServiceStatusHandle == NULL) + { + LOG(ERROR) << "Handler not install"; + return; + } + + SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus); + + g_ServiceStatus.dwWin32ExitCode = S_OK; + g_ServiceStatus.dwCheckPoint = 0; + g_ServiceStatus.dwWaitHint = 0; + g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus); + + // 在 ServiceMain 中调用控制台程序的主函数 + ConsoleMain(); + + // 设置服务状态为停止 + g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + g_ServiceStatus.dwCheckPoint = 0; + g_ServiceStatus.dwWaitHint = 0; + + if (!SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus)){ + LOG(ERROR) << "SetServiceStatus failed, error code: " << GetLastError(); + } + return; +} + +void Init() +{ + g_ServiceStatusHandle = NULL; + g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + g_ServiceStatus.dwWin32ExitCode = 0; + g_ServiceStatus.dwServiceSpecificExitCode = 0; + g_ServiceStatus.dwCheckPoint = 0; + g_ServiceStatus.dwWaitHint = 0; +} + + +bool IsInstalled() +{ + SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hSCM == NULL) { + LOG(ERROR) << "OpenSCManager error..."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 Q!t|M6mhdX7+gnBcKbHN-`~Uy| literal 0 HcmV?d00001 diff --git a/output/Release/install.bat b/output/Release/install.bat new file mode 100644 index 0000000..ee513af --- /dev/null +++ b/output/Release/install.bat @@ -0,0 +1,8 @@ + +cd /d "%~dp0" +echo Current directory: %CD% + +MyPcap.exe /install + + +pause diff --git a/output/Release/notify.webp b/output/Release/notify.webp new file mode 100644 index 0000000000000000000000000000000000000000..d49035aeba7157417c785b1a3df221776de0a7aa GIT binary patch literal 3704 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 WLHU~`Lch3Yp@(iS006rN0002^l|Bvt literal 0 HcmV?d00001 diff --git a/output/Release/uninstall.bat b/output/Release/uninstall.bat new file mode 100644 index 0000000..7fadd02 --- /dev/null +++ b/output/Release/uninstall.bat @@ -0,0 +1,9 @@ + +cd /d "%~dp0" +echo Current directory: %CD% + +MyPcap.exe /uninstall +pause + + + diff --git a/output/Release/wpcap.dll b/output/Release/wpcap.dll new file mode 100644 index 0000000000000000000000000000000000000000..6ca7a6a83f7b86324b556ccb72f91705e800831d GIT binary patch literal 489424 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�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 z�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 literal 0 HcmV?d00001 diff --git a/small.ico b/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 diff --git a/utils/LocalAddr.h b/utils/LocalAddr.h new file mode 100644 index 0000000..9729211 --- /dev/null +++ b/utils/LocalAddr.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#pragma comment(lib,"ws2_32.lib") //连接ws2_32.lib库.只要程序中用到Winsock API 函数,都要用到 Ws2_32.lib +#pragma comment(lib, "iphlpapi.lib") + +class LocalAddr { + +private: + void FreeIpForwardTable(PMIB_IPFORWARDTABLE pIpRouteTab) + { + if (pIpRouteTab != NULL) + { + ::GlobalFree(pIpRouteTab); + pIpRouteTab = NULL; + } + } + + PMIB_IPFORWARDTABLE GetIpForwardTable(BOOL bOrder) + { + PMIB_IPFORWARDTABLE pIpRouteTab = NULL; + DWORD dwActualSize = 0; + + // 查询所需缓冲区的大小 + if (::GetIpForwardTable(pIpRouteTab, &dwActualSize, bOrder) == ERROR_INSUFFICIENT_BUFFER) + { + // 为MIB_IPFORWARDTABLE结构申请内存 + pIpRouteTab = (PMIB_IPFORWARDTABLE)::GlobalAlloc(GPTR, dwActualSize); + // 获取路由表 + if (::GetIpForwardTable(pIpRouteTab, &dwActualSize, bOrder) == NO_ERROR) + return pIpRouteTab; + ::GlobalFree(pIpRouteTab); + } + return NULL; + } + +public: + std::string GetSystemIpAddress() + { + std::string strLoalhostIp; + PMIB_IPFORWARDTABLE pIpRouteTable = GetIpForwardTable(TRUE); + if (pIpRouteTable != NULL) + { + DWORD dwCurrIndex; + struct in_addr inadDest; + struct in_addr inadMask; + struct in_addr inadGateway; + char szDestIp[128] = { 0 }; + char szMaskIp[128] = { 0 }; + char szGatewayIp[128] = { 0 }; + DWORD IfIndex = 0; + DWORD ForwardMetric1 = 0; + if (pIpRouteTable->dwNumEntries > 0) + { + int i = 0; + for (i = 0; i < pIpRouteTable->dwNumEntries; i++) + { + dwCurrIndex = pIpRouteTable->table[i].dwForwardIfIndex; + // 目的地址 + inadDest.s_addr = pIpRouteTable->table[i].dwForwardDest; + strcpy_s(szDestIp, sizeof(szDestIp), inet_ntoa(inadDest)); + // 子网掩码 + inadMask.s_addr = pIpRouteTable->table[i].dwForwardMask; + strcpy_s(szMaskIp, sizeof(szDestIp), inet_ntoa(inadMask)); + // 网关地址 + inadGateway.s_addr = pIpRouteTable->table[i].dwForwardNextHop; + strcpy_s(szGatewayIp, sizeof(szDestIp), inet_ntoa(inadGateway)); + if ((strcmp(szDestIp, "0.0.0.0") == 0) && (strcmp(szMaskIp, "0.0.0.0") == 0)) + { + + if (i == 0) + { + ForwardMetric1 = pIpRouteTable->table[i].dwForwardMetric1; + IfIndex = pIpRouteTable->table[i].dwForwardIfIndex; + struct in_addr inadDest; + inadDest.s_addr = pIpRouteTable->table[i].dwForwardDest; + } + else if (ForwardMetric1 > pIpRouteTable->table[i].dwForwardMetric1) + { + ForwardMetric1 = pIpRouteTable->table[i].dwForwardMetric1; + IfIndex = pIpRouteTable->table[i].dwForwardIfIndex; + struct in_addr inadDest; + inadDest.s_addr = pIpRouteTable->table[i].dwForwardDest; + } + } + } + } + else + { + FreeIpForwardTable(pIpRouteTable); + return ""; + } + FreeIpForwardTable(pIpRouteTable); + + + if (IfIndex > 0) + { + DWORD ipdwSize = 0; + PBYTE m_pBuffer = new BYTE[MAX_PATH]; + ULONG m_ulSize = MAX_PATH; + DWORD m_dwResult; + PMIB_IPADDRTABLE pAddrTable; + PMIB_IPADDRROW pAddrRow; + in_addr ia; + GetIpAddrTable((PMIB_IPADDRTABLE)m_pBuffer, &m_ulSize, TRUE); + delete[] m_pBuffer; + m_pBuffer = new BYTE[m_ulSize]; + if (NULL != m_pBuffer) + { + m_dwResult = GetIpAddrTable((PMIB_IPADDRTABLE)m_pBuffer, &m_ulSize, TRUE); + if (m_dwResult == NO_ERROR) + { + pAddrTable = (PMIB_IPADDRTABLE)m_pBuffer; + + for (int x = 0; x < pAddrTable->dwNumEntries; x++) + { + pAddrRow = (PMIB_IPADDRROW) & (pAddrTable->table[x]); + + ia.S_un.S_addr = pAddrRow->dwAddr; + char IPMsg[100] = { 0 }; + if (IfIndex == pAddrRow->dwIndex) + { + LPCSTR psz = inet_ntoa(ia); + if (psz) + { + strLoalhostIp = psz; + } + delete[] m_pBuffer; + return strLoalhostIp; + } + } + } + delete[] m_pBuffer; + } + } + } + else + { + FreeIpForwardTable(pIpRouteTable); + return ""; + } + return ""; + } + +}; \ No newline at end of file diff --git a/wintoastlib.cpp b/wintoastlib.cpp new file mode 100644 index 0000000..e57b1b2 --- /dev/null +++ b/wintoastlib.cpp @@ -0,0 +1,1367 @@ +/** + * MIT License + * + * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "wintoastlib.h" + +#include +#include +#include +#include +#include + +#pragma comment(lib, "shlwapi") +#pragma comment(lib, "user32") + +#ifdef NDEBUG +# define DEBUG_MSG(str) \ + do { \ + } while (false) +#else +# define DEBUG_MSG(str) \ + do { \ + std::wcout << str << std::endl; \ + } while (false) +#endif + +#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" +#define DEFAULT_LINK_FORMAT L".lnk" +#define STATUS_SUCCESS (0x00000000) + + // Quickstart: Handling toast activations from Win32 apps in Windows 10 + // https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ +using namespace WinToastLib; +namespace DllImporter { + + // Function load a function from library + template + HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function& func) { + if (!library) { + return E_INVALIDARG; + } + func = reinterpret_cast(GetProcAddress(library, name)); + return (func != nullptr) ? S_OK : E_FAIL; + } + + typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); + typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); + typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, + _COM_Outptr_ void** factory); + typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, + _Out_ HSTRING_HEADER* hstringHeader, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); + typedef PCWSTR(FAR STDAPICALLTYPE* f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_opt_ UINT32* length); + typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)(_In_opt_ HSTRING string); + + static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; + static f_PropVariantToString PropVariantToString; + static f_RoGetActivationFactory RoGetActivationFactory; + static f_WindowsCreateStringReference WindowsCreateStringReference; + static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; + static f_WindowsDeleteString WindowsDeleteString; + + template + __inline _Check_return_ HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); + } + + template + inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) noexcept { + return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); + } + + inline HRESULT initialize() { + HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); + HRESULT hr = + loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); + if (SUCCEEDED(hr)) { + HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); + hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); + if (SUCCEEDED(hr)) { + HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); + bool const succeded = + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); + return succeded ? S_OK : E_FAIL; + } + } + return hr; + } +} // namespace DllImporter + +class WinToastStringWrapper { +public: + WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) noexcept { + HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + WinToastStringWrapper(_In_ std::wstring const& stringRef) noexcept { + HRESULT hr = + DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + ~WinToastStringWrapper() { + DllImporter::WindowsDeleteString(_hstring); + } + + inline HSTRING Get() const noexcept { + return _hstring; + } + +private: + HSTRING _hstring; + HSTRING_HEADER _header; +}; + +class InternalDateTime : public IReference { +public: + static INT64 Now() { + FILETIME now; + GetSystemTimeAsFileTime(&now); + return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); + } + + InternalDateTime(DateTime dateTime) : _dateTime(dateTime) {} + + InternalDateTime(INT64 millisecondsFromNow) { + _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; + } + + virtual ~InternalDateTime() = default; + + operator INT64() { + return _dateTime.UniversalTime; + } + + HRESULT STDMETHODCALLTYPE get_Value(DateTime* dateTime) { + *dateTime = _dateTime; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& riid, void** ppvObject) { + if (!ppvObject) { + return E_POINTER; + } + if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference)) { + *ppvObject = static_cast(static_cast*>(this)); + return S_OK; + } + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE Release() { + return 1; + } + + ULONG STDMETHODCALLTYPE AddRef() { + return 2; + } + + HRESULT STDMETHODCALLTYPE GetIids(ULONG*, IID**) { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING*) { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel*) { + return E_NOTIMPL; + } + +protected: + DateTime _dateTime; +}; + +namespace Util { + + typedef LONG NTSTATUS, * PNTSTATUS; + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + inline RTL_OSVERSIONINFOW getRealOSVersion() { + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + RTL_OSVERSIONINFOW rovi = { 0 }; + rovi.dwOSVersionInfoSize = sizeof(rovi); + if (STATUS_SUCCESS == fxPtr(&rovi)) { + return rovi; + } + } + } + RTL_OSVERSIONINFOW rovi = { 0 }; + return rovi; + } + + inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); + DEBUG_MSG("Default executable path: " << path); + return (written > 0) ? S_OK : E_FAIL; + } + + inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); + HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; + if (SUCCEEDED(hr)) { + errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); + hr = (result == 0) ? S_OK : E_INVALIDARG; + DEBUG_MSG("Default shell link path: " << path); + } + return hr; + } + + inline HRESULT defaultShellLinkPath(_In_ std::wstring const& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + HRESULT hr = defaultShellLinksDirectory(path, nSize); + if (SUCCEEDED(hr)) { + const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); + errno_t result = wcscat_s(path, nSize, appLink.c_str()); + hr = (result == 0) ? S_OK : E_INVALIDARG; + DEBUG_MSG("Default shell link file path: " << path); + } + return hr; + } + + inline PCWSTR AsString(_In_ ComPtr& xmlDocument) { + HSTRING xml; + ComPtr ser; + HRESULT hr = xmlDocument.As(&ser); + hr = ser->GetXml(&xml); + if (SUCCEEDED(hr)) { + return DllImporter::WindowsGetStringRawBuffer(xml, nullptr); + } + return nullptr; + } + + inline PCWSTR AsString(_In_ HSTRING hstring) { + return DllImporter::WindowsGetStringRawBuffer(hstring, nullptr); + } + + inline HRESULT setNodeStringValue(_In_ std::wstring const& string, _Out_opt_ IXmlNode* node, _Out_ IXmlDocument* xml) { + ComPtr textNode; + HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); + if (SUCCEEDED(hr)) { + ComPtr stringNode; + hr = textNode.As(&stringNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = node->AppendChild(stringNode.Get(), &appendedChild); + } + } + return hr; + } + + template + inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ std::shared_ptr eventHandler, + _In_ INT64 expirationTime, _Out_ EventRegistrationToken& activatedToken, + _Out_ EventRegistrationToken& dismissedToken, _Out_ EventRegistrationToken& failedToken, + _In_ FunctorT&& markAsReadyForDeletionFunc) { + HRESULT hr = notification->add_Activated( + Callback, ITypedEventHandler>>( + [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IInspectable* inspectable) { + ComPtr activatedEventArgs; + HRESULT hr = inspectable->QueryInterface(activatedEventArgs.GetAddressOf()); + if (SUCCEEDED(hr)) { + HSTRING argumentsHandle; + hr = activatedEventArgs->get_Arguments(&argumentsHandle); + if (SUCCEEDED(hr)) { + PCWSTR arguments = Util::AsString(argumentsHandle); + if (arguments && *arguments) { + eventHandler->toastActivated(static_cast(wcstol(arguments, nullptr, 10))); + DllImporter::WindowsDeleteString(argumentsHandle); + markAsReadyForDeletionFunc(); + return S_OK; + } + DllImporter::WindowsDeleteString(argumentsHandle); + } + } + eventHandler->toastActivated(); + markAsReadyForDeletionFunc(); + return S_OK; + }) + .Get(), + &activatedToken); + + if (SUCCEEDED(hr)) { + hr = notification->add_Dismissed( + Callback, ITypedEventHandler>>( + [eventHandler, expirationTime, markAsReadyForDeletionFunc](IToastNotification* notify, IToastDismissedEventArgs* e) { + ToastDismissalReason reason; + if (SUCCEEDED(e->get_Reason(&reason))) { + if (reason == ToastDismissalReason_UserCanceled && expirationTime && + InternalDateTime::Now() >= expirationTime) { + reason = ToastDismissalReason_TimedOut; + } + eventHandler->toastDismissed(static_cast(reason)); + } + markAsReadyForDeletionFunc(); + return S_OK; + }) + .Get(), + &dismissedToken); + if (SUCCEEDED(hr)) { + hr = notification->add_Failed( + Callback, ITypedEventHandler>>( + [eventHandler, markAsReadyForDeletionFunc](IToastNotification* notify, IToastFailedEventArgs* e) { + eventHandler->toastFailed(); + markAsReadyForDeletionFunc(); + return S_OK; + }) + .Get(), + &failedToken); + } + } + return hr; + } + + inline HRESULT addAttribute(_In_ IXmlDocument* xml, std::wstring const& name, IXmlNamedNodeMap* attributeMap) { + ComPtr srcAttribute; + HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = srcAttribute.As(&node); + if (SUCCEEDED(hr)) { + ComPtr pNode; + hr = attributeMap->SetNamedItem(node.Get(), &pNode); + } + } + return hr; + } + + inline HRESULT createElement(_In_ IXmlDocument* xml, _In_ std::wstring const& root_node, _In_ std::wstring const& element_name, + _In_ std::vector const& attribute_names) { + ComPtr rootList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); + if (SUCCEEDED(hr)) { + ComPtr root; + hr = rootList->Item(0, &root); + if (SUCCEEDED(hr)) { + ComPtr audioElement; + hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); + if (SUCCEEDED(hr)) { + ComPtr audioNodeTmp; + hr = audioElement.As(&audioNodeTmp); + if (SUCCEEDED(hr)) { + ComPtr audioNode; + hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = audioNode->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + for (auto const& it : attribute_names) { + hr = addAttribute(xml, it, attributes.Get()); + } + } + } + } + } + } + } + return hr; + } +} // namespace Util + +WinToast* WinToast::instance() { + static WinToast instance; + return &instance; +} + +WinToast::WinToast() : _isInitialized(false), _hasCoInitialized(false) { + if (!isCompatible()) { + DEBUG_MSG(L"Warning: Your system is not compatible with this library "); + } +} + +WinToast::~WinToast() { + clear(); + + if (_hasCoInitialized) { + CoUninitialize(); + } +} + +void WinToast::setAppName(_In_ std::wstring const& appName) { + _appName = appName; +} + +void WinToast::setAppUserModelId(_In_ std::wstring const& aumi) { + _aumi = aumi; + DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); +} + +void WinToast::setShortcutPolicy(_In_ ShortcutPolicy shortcutPolicy) { + _shortcutPolicy = shortcutPolicy; +} + +bool WinToast::isCompatible() { + DllImporter::initialize(); + return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || + (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || + (DllImporter::WindowsDeleteString == nullptr)); +} + +bool WinToastLib::WinToast::isSupportingModernFeatures() { + constexpr auto MinimumSupportedVersion = 6; + return Util::getRealOSVersion().dwMajorVersion > MinimumSupportedVersion; +} + +bool WinToastLib::WinToast::isWin10AnniversaryOrHigher() { + return Util::getRealOSVersion().dwBuildNumber >= 14393; +} + +std::wstring WinToast::configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, + _In_ std::wstring const& subProduct, _In_ std::wstring const& versionInformation) { + std::wstring aumi = companyName; + aumi += L"." + productName; + if (subProduct.length() > 0) { + aumi += L"." + subProduct; + if (versionInformation.length() > 0) { + aumi += L"." + versionInformation; + } + } + + if (aumi.length() > SCHAR_MAX) { + DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); + } + return aumi; +} + +std::wstring const& WinToast::strerror(WinToastError error) { + static const std::unordered_map Labels = { + {WinToastError::NoError, L"No error. The process was executed correctly" }, + {WinToastError::NotInitialized, L"The library has not been initialized" }, + {WinToastError::SystemNotSupported, L"The OS does not support WinToast" }, + {WinToastError::ShellLinkNotCreated, L"The library was not able to create a Shell Link for the app" }, + {WinToastError::InvalidAppUserModelID, L"The AUMI is not a valid one" }, + {WinToastError::InvalidParameters, L"Invalid parameters, please double-check the AUMI or App Name" }, + {WinToastError::NotDisplayed, L"The toast was created correctly but WinToast was not able to display the toast"}, + {WinToastError::UnknownError, L"Unknown error" } + }; + + auto const iter = Labels.find(error); + assert(iter != Labels.end()); + return iter->second; +} + +enum WinToast::ShortcutResult WinToast::createShortcut() { + if (_aumi.empty() || _appName.empty()) { + DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); + return SHORTCUT_MISSING_PARAMETERS; + } + + if (!isCompatible()) { + DEBUG_MSG(L"Your OS is not compatible with this library! =("); + return SHORTCUT_INCOMPATIBLE_OS; + } + + if (!_hasCoInitialized) { + HRESULT initHr = CoInitializeEx(nullptr, COINIT::COINIT_MULTITHREADED); + if (initHr != RPC_E_CHANGED_MODE) { + if (FAILED(initHr) && initHr != S_FALSE) { + DEBUG_MSG(L"Error on COM library initialization!"); + return SHORTCUT_COM_INIT_FAILURE; + } + else { + _hasCoInitialized = true; + } + } + } + + bool wasChanged; + HRESULT hr = validateShellLinkHelper(wasChanged); + if (SUCCEEDED(hr)) { + return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; + } + + hr = createShellLinkHelper(); + return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; +} + +bool WinToast::initialize(_Out_opt_ WinToastError* error) { + _isInitialized = false; + setError(error, WinToastError::NoError); + + if (!isCompatible()) { + setError(error, WinToastError::SystemNotSupported); + DEBUG_MSG(L"Error: system not supported."); + return false; + } + + if (_aumi.empty() || _appName.empty()) { + setError(error, WinToastError::InvalidParameters); + DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); + return false; + } + + if (_shortcutPolicy != SHORTCUT_POLICY_IGNORE) { + if (createShortcut() < 0) { + setError(error, WinToastError::ShellLinkNotCreated); + DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); + return false; + } + } + + if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { + setError(error, WinToastError::InvalidAppUserModelID); + DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); + return false; + } + + _isInitialized = true; + return _isInitialized; +} + +bool WinToast::isInitialized() const { + return _isInitialized; +} + +std::wstring const& WinToast::appName() const { + return _appName; +} + +std::wstring const& WinToast::appUserModelId() const { + return _aumi; +} + +HRESULT WinToast::validateShellLinkHelper(_Out_ bool& wasChanged) { + WCHAR path[MAX_PATH] = { L'\0' }; + Util::defaultShellLinkPath(_appName, path); + // Check if the file exist + DWORD attr = GetFileAttributesW(path); + if (attr >= 0xFFFFFFF) { + DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); + return E_FAIL; + } + + // Let's load the file as shell link to validate. + // - Create a shell link + // - Create a persistant file + // - Load the path as data for the persistant file + // - Read the property AUMI and validate with the current + // - Review if AUMI is equal. + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Load(path, STGM_READWRITE); + if (SUCCEEDED(hr)) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (SUCCEEDED(hr)) { + PROPVARIANT appIdPropVar; + hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); + if (SUCCEEDED(hr)) { + WCHAR AUMI[MAX_PATH]; + hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); + wasChanged = false; + if (FAILED(hr) || _aumi != AUMI) { + if (_shortcutPolicy == SHORTCUT_POLICY_REQUIRE_CREATE) { + // AUMI Changed for the same app, let's update the current value! =) + wasChanged = true; + PropVariantClear(&appIdPropVar); + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); + if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { + hr = persistFile->Save(path, TRUE); + } + } + } + } + else { + // Not allowed to touch the shortcut to fix the AUMI + hr = E_FAIL; + } + } + PropVariantClear(&appIdPropVar); + } + } + } + } + } + return hr; +} + +HRESULT WinToast::createShellLinkHelper() { + if (_shortcutPolicy != SHORTCUT_POLICY_REQUIRE_CREATE) { + return E_FAIL; + } + + WCHAR exePath[MAX_PATH]{ L'\0' }; + WCHAR slPath[MAX_PATH]{ L'\0' }; + Util::defaultShellLinkPath(_appName, slPath); + Util::defaultExecutablePath(exePath); + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + hr = shellLink->SetPath(exePath); + if (SUCCEEDED(hr)) { + hr = shellLink->SetArguments(L""); + if (SUCCEEDED(hr)) { + hr = shellLink->SetWorkingDirectory(exePath); + if (SUCCEEDED(hr)) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (SUCCEEDED(hr)) { + PROPVARIANT appIdPropVar; + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); + if (SUCCEEDED(hr)) { + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Save(slPath, TRUE); + } + } + } + PropVariantClear(&appIdPropVar); + } + } + } + } + } + } + return hr; +} + +INT64 WinToast::showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, _Out_ WinToastError* error) { + std::shared_ptr handler(eventHandler); + setError(error, WinToastError::NoError); + INT64 id = -1; + if (!isInitialized()) { + setError(error, WinToastError::NotInitialized); + DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); + return id; + } + if (!handler) { + setError(error, WinToastError::InvalidHandler); + DEBUG_MSG("Error when launching the toast. Handler cannot be nullptr."); + return id; + } + + ComPtr notificationManager; + HRESULT hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + if (SUCCEEDED(hr)) { + ComPtr notifier; + hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); + if (SUCCEEDED(hr)) { + ComPtr notificationFactory; + hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); + if (SUCCEEDED(hr)) { + ComPtr xmlDocument; + hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); + if (SUCCEEDED(hr) && toast.isToastGeneric()) { + hr = setBindToastGenericHelper(xmlDocument.Get()); + } + if (SUCCEEDED(hr)) { + for (UINT32 i = 0, fieldsCount = static_cast(toast.textFieldsCount()); i < fieldsCount && SUCCEEDED(hr); i++) { + hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); + } + + // Modern feature are supported Windows > Windows 10 + if (SUCCEEDED(hr) && isSupportingModernFeatures()) { + // Note that we do this *after* using toast.textFieldsCount() to + // iterate/fill the template's text fields, since we're adding yet another text field. + if (SUCCEEDED(hr) && !toast.attributionText().empty()) { + hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); + } + + std::array buf; + for (std::size_t i = 0, actionsCount = toast.actionsCount(); i < actionsCount && SUCCEEDED(hr); i++) { + _snwprintf_s(buf.data(), buf.size(), _TRUNCATE, L"%zd", i); + hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf.data()); + } + + if (SUCCEEDED(hr)) { + hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) + ? hr + : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); + } + + if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { + hr = addDurationHelper(xmlDocument.Get(), + (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); + } + + if (SUCCEEDED(hr)) { + hr = addScenarioHelper(xmlDocument.Get(), toast.scenario()); + } + + } + else { + DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); + } + + if (SUCCEEDED(hr)) { + bool isWin10AnniversaryOrAbove = WinToast::isWin10AnniversaryOrHigher(); + bool isCircleCropHint = isWin10AnniversaryOrAbove ? toast.isCropHintCircle() : false; + hr = toast.hasImage() + ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath(), toast.isToastGeneric(), isCircleCropHint) + : hr; + if (SUCCEEDED(hr) && isWin10AnniversaryOrAbove && toast.hasHeroImage()) { + hr = setHeroImageHelper(xmlDocument.Get(), toast.heroImagePath(), toast.isInlineHeroImage()); + } + if (SUCCEEDED(hr)) { + ComPtr notification; + hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); + if (SUCCEEDED(hr)) { + INT64 expiration = 0, relativeExpiration = toast.expiration(); + if (relativeExpiration > 0) { + InternalDateTime expirationDateTime(relativeExpiration); + expiration = expirationDateTime; + hr = notification->put_ExpirationTime(&expirationDateTime); + } + + EventRegistrationToken activatedToken, dismissedToken, failedToken; + + GUID guid; + HRESULT hrGuid = CoCreateGuid(&guid); + id = guid.Data1; + if (SUCCEEDED(hr) && SUCCEEDED(hrGuid)) { + hr = Util::setEventHandlers(notification.Get(), handler, expiration, activatedToken, dismissedToken, + failedToken, [this, id]() { markAsReadyForDeletion(id); }); + if (FAILED(hr)) { + setError(error, WinToastError::InvalidHandler); + } + } + + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) { + _buffer.emplace(id, NotifyData(notification, activatedToken, dismissedToken, failedToken)); + DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); + hr = notifier->Show(notification.Get()); + if (FAILED(hr)) { + setError(error, WinToastError::NotDisplayed); + } + } + } + } + } + } + } + } + } + } + return FAILED(hr) ? -1 : id; +} + +ComPtr WinToast::notifier(_In_ bool* succeded) const { + ComPtr notificationManager; + ComPtr notifier; + HRESULT hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); + if (SUCCEEDED(hr)) { + hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); + } + *succeded = SUCCEEDED(hr); + return notifier; +} + +void WinToast::markAsReadyForDeletion(_In_ INT64 id) { + // Flush the buffer by removing all the toasts that are ready for deletion + for (auto it = _buffer.begin(); it != _buffer.end();) { + if (it->second.isReadyForDeletion()) { + it->second.RemoveTokens(); + it = _buffer.erase(it); + } + else { + ++it; + } + } + + // Mark the toast as ready for deletion (if it exists) so that it will be removed from the buffer in the next iteration + auto const iter = _buffer.find(id); + if (iter != _buffer.end()) { + _buffer[id].markAsReadyForDeletion(); + } +} + +bool WinToast::hideToast(_In_ INT64 id) { + if (!isInitialized()) { + DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); + return false; + } + + auto iter = _buffer.find(id); + if (iter == _buffer.end()) { + return false; + } + + auto succeded = false; + auto notify = notifier(&succeded); + if (!succeded) { + return false; + } + + auto& notifyData = iter->second; + auto result = notify->Hide(notifyData.notification()); + if (FAILED(result)) { + DEBUG_MSG("Error when hiding the toast. Error code: " << result); + return false; + } + + notifyData.RemoveTokens(); + _buffer.erase(iter); + return SUCCEEDED(result); +} + +void WinToast::clear() { + auto succeded = false; + auto notify = notifier(&succeded); + if (!succeded) { + return; + } + + for (auto& data : _buffer) { + auto& notifyData = data.second; + notify->Hide(notifyData.notification()); + notifyData.RemoveTokens(); + } + _buffer.clear(); +} + +// +// Available as of Windows 10 Anniversary Update +// Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts +// +// NOTE: This will add a new text field, so be aware when iterating over +// the toast's text fields or getting a count of them. +// +HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text) { + Util::createElement(xml, L"binding", L"text", { L"placement" }); + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 nodeListLength; + hr = nodeList->get_Length(&nodeListLength); + if (SUCCEEDED(hr)) { + for (UINT32 i = 0; i < nodeListLength; i++) { + ComPtr textNode; + hr = nodeList->Item(i, &textNode); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = textNode->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + ComPtr editedNode; + if (SUCCEEDED(hr)) { + hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); + if (FAILED(hr) || !editedNode) { + continue; + } + hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); + if (SUCCEEDED(hr)) { + return setTextFieldHelper(xml, text, i); + } + } + } + } + } + } + } + return hr; +} + +HRESULT WinToast::addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 length; + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr toastElement; + hr = toastNode.As(&toastElement); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); + } + } + } + } + return hr; +} + +HRESULT WinToast::addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 length; + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr toastElement; + hr = toastNode.As(&toastElement); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"scenario").Get(), WinToastStringWrapper(scenario).Get()); + } + } + } + } + return hr; +} + +HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(pos, &node); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(text, node.Get(), xml); + } + } + return hr; +} + +HRESULT WinToast::setBindToastGenericHelper(_In_ IXmlDocument* xml) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 length; + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr toastElement; + hr = toastNode.As(&toastElement); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); + } + } + } + } + return hr; +} + +HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, + _In_ bool isCropHintCircle) { + assert(path.size() < MAX_PATH); + + wchar_t imagePath[MAX_PATH] = L"file:///"; + HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); + if (SUCCEEDED(hr)) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(0, &node); + + ComPtr imageElement; + HRESULT hrImage = node.As(&imageElement); + if (SUCCEEDED(hr) && SUCCEEDED(hrImage) && isToastGeneric) { + hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"appLogoOverride").Get()); + if (SUCCEEDED(hr) && isCropHintCircle) { + hr = imageElement->SetAttribute(WinToastStringWrapper(L"hint-crop").Get(), WinToastStringWrapper(L"circle").Get()); + } + } + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = node->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + ComPtr editedNode; + hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); + if (SUCCEEDED(hr)) { + Util::setNodeStringValue(imagePath, editedNode.Get(), xml); + } + } + } + } + } + return hr; +} + +HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, + _In_opt_ WinToastTemplate::AudioOption option) { + std::vector attrs; + if (!path.empty()) { + attrs.push_back(L"src"); + } + if (option == WinToastTemplate::AudioOption::Loop) { + attrs.push_back(L"loop"); + } + if (option == WinToastTemplate::AudioOption::Silent) { + attrs.push_back(L"silent"); + } + Util::createElement(xml, L"toast", L"audio", attrs); + + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(0, &node); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = node->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + ComPtr editedNode; + if (!path.empty()) { + if (SUCCEEDED(hr)) { + hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(path, editedNode.Get(), xml); + } + } + } + + if (SUCCEEDED(hr)) { + switch (option) { + case WinToastTemplate::AudioOption::Loop: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + break; + case WinToastTemplate::AudioOption::Silent: + hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); + } + default: + break; + } + } + } + } + } + return hr; +} + +HRESULT WinToast::addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& content, _In_ std::wstring const& arguments) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 length; + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr actionsNode; + if (length > 0) { + hr = nodeList->Item(0, &actionsNode); + } + else { + hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); + if (SUCCEEDED(hr)) { + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (SUCCEEDED(hr)) { + ComPtr toastElement; + hr = toastNode.As(&toastElement); + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), + WinToastStringWrapper(L"ToastGeneric").Get()); + } + if (SUCCEEDED(hr)) { + hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), + WinToastStringWrapper(L"long").Get()); + } + if (SUCCEEDED(hr)) { + ComPtr actionsElement; + hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); + if (SUCCEEDED(hr)) { + hr = actionsElement.As(&actionsNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); + } + } + } + } + } + } + } + if (SUCCEEDED(hr)) { + ComPtr actionElement; + hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); + if (SUCCEEDED(hr)) { + hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); + } + if (SUCCEEDED(hr)) { + hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); + } + if (SUCCEEDED(hr)) { + ComPtr actionNode; + hr = actionElement.As(&actionNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); + } + } + } + } + } + return hr; +} + +HRESULT WinToast::setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage) { + ComPtr nodeList; + HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"binding").Get(), &nodeList); + if (SUCCEEDED(hr)) { + UINT32 length; + hr = nodeList->get_Length(&length); + if (SUCCEEDED(hr)) { + ComPtr bindingNode; + if (length > 0) { + hr = nodeList->Item(0, &bindingNode); + } + if (SUCCEEDED(hr)) { + ComPtr imageElement; + hr = xml->CreateElement(WinToastStringWrapper(L"image").Get(), &imageElement); + if (SUCCEEDED(hr) && isInlineImage == false) { + hr = imageElement->SetAttribute(WinToastStringWrapper(L"placement").Get(), WinToastStringWrapper(L"hero").Get()); + } + if (SUCCEEDED(hr)) { + hr = imageElement->SetAttribute(WinToastStringWrapper(L"src").Get(), WinToastStringWrapper(path).Get()); + } + if (SUCCEEDED(hr)) { + ComPtr actionNode; + hr = imageElement.As(&actionNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = bindingNode->AppendChild(actionNode.Get(), &appendedChild); + } + } + } + } + } + return hr; +} + +void WinToast::setError(_Out_opt_ WinToastError* error, _In_ WinToastError value) { + if (error) { + *error = value; + } +} + +WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) : _type(type) { + constexpr static std::size_t TextFieldsCount[] = { 1, 2, 2, 3, 1, 2, 2, 3 }; + _textFields = std::vector(TextFieldsCount[type], L""); +} + +WinToastTemplate::~WinToastTemplate() { + _textFields.clear(); +} + +void WinToastTemplate::setTextField(_In_ std::wstring const& txt, _In_ WinToastTemplate::TextField pos) { + auto const position = static_cast(pos); + if (position >= _textFields.size()) { + DEBUG_MSG("The selected template type supports only " << _textFields.size() << " text lines"); + return; + } + _textFields[position] = txt; +} + +void WinToastTemplate::setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint) { + _imagePath = imgPath; + _cropHint = cropHint; +} + +void WinToastTemplate::setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage) { + _heroImagePath = imgPath; + _inlineHeroImage = inlineImage; +} + +void WinToastTemplate::setAudioPath(_In_ std::wstring const& audioPath) { + _audioPath = audioPath; +} + +void WinToastTemplate::setAudioPath(_In_ AudioSystemFile file) { + static const std::unordered_map Files = { + {AudioSystemFile::DefaultSound, L"ms-winsoundevent:Notification.Default" }, + {AudioSystemFile::IM, L"ms-winsoundevent:Notification.IM" }, + {AudioSystemFile::Mail, L"ms-winsoundevent:Notification.Mail" }, + {AudioSystemFile::Reminder, L"ms-winsoundevent:Notification.Reminder" }, + {AudioSystemFile::SMS, L"ms-winsoundevent:Notification.SMS" }, + {AudioSystemFile::Alarm, L"ms-winsoundevent:Notification.Looping.Alarm" }, + {AudioSystemFile::Alarm2, L"ms-winsoundevent:Notification.Looping.Alarm2" }, + {AudioSystemFile::Alarm3, L"ms-winsoundevent:Notification.Looping.Alarm3" }, + {AudioSystemFile::Alarm4, L"ms-winsoundevent:Notification.Looping.Alarm4" }, + {AudioSystemFile::Alarm5, L"ms-winsoundevent:Notification.Looping.Alarm5" }, + {AudioSystemFile::Alarm6, L"ms-winsoundevent:Notification.Looping.Alarm6" }, + {AudioSystemFile::Alarm7, L"ms-winsoundevent:Notification.Looping.Alarm7" }, + {AudioSystemFile::Alarm8, L"ms-winsoundevent:Notification.Looping.Alarm8" }, + {AudioSystemFile::Alarm9, L"ms-winsoundevent:Notification.Looping.Alarm9" }, + {AudioSystemFile::Alarm10, L"ms-winsoundevent:Notification.Looping.Alarm10"}, + {AudioSystemFile::Call, L"ms-winsoundevent:Notification.Looping.Call" }, + {AudioSystemFile::Call1, L"ms-winsoundevent:Notification.Looping.Call1" }, + {AudioSystemFile::Call2, L"ms-winsoundevent:Notification.Looping.Call2" }, + {AudioSystemFile::Call3, L"ms-winsoundevent:Notification.Looping.Call3" }, + {AudioSystemFile::Call4, L"ms-winsoundevent:Notification.Looping.Call4" }, + {AudioSystemFile::Call5, L"ms-winsoundevent:Notification.Looping.Call5" }, + {AudioSystemFile::Call6, L"ms-winsoundevent:Notification.Looping.Call6" }, + {AudioSystemFile::Call7, L"ms-winsoundevent:Notification.Looping.Call7" }, + {AudioSystemFile::Call8, L"ms-winsoundevent:Notification.Looping.Call8" }, + {AudioSystemFile::Call9, L"ms-winsoundevent:Notification.Looping.Call9" }, + {AudioSystemFile::Call10, L"ms-winsoundevent:Notification.Looping.Call10" }, + }; + auto const iter = Files.find(file); + assert(iter != Files.end()); + _audioPath = iter->second; +} + +void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) { + _audioOption = audioOption; +} + +void WinToastTemplate::setFirstLine(_In_ std::wstring const& text) { + setTextField(text, WinToastTemplate::FirstLine); +} + +void WinToastTemplate::setSecondLine(_In_ std::wstring const& text) { + setTextField(text, WinToastTemplate::SecondLine); +} + +void WinToastTemplate::setThirdLine(_In_ std::wstring const& text) { + setTextField(text, WinToastTemplate::ThirdLine); +} + +void WinToastTemplate::setDuration(_In_ Duration duration) { + _duration = duration; +} + +void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) { + _expiration = millisecondsFromNow; +} + +void WinToastLib::WinToastTemplate::setScenario(_In_ Scenario scenario) { + switch (scenario) { + case Scenario::Default: + _scenario = L"Default"; + break; + case Scenario::Alarm: + _scenario = L"Alarm"; + break; + case Scenario::IncomingCall: + _scenario = L"IncomingCall"; + break; + case Scenario::Reminder: + _scenario = L"Reminder"; + break; + } +} + +void WinToastTemplate::setAttributionText(_In_ std::wstring const& attributionText) { + _attributionText = attributionText; +} + +void WinToastTemplate::addAction(_In_ std::wstring const& label) { + _actions.push_back(label); +} + +std::size_t WinToastTemplate::textFieldsCount() const { + return _textFields.size(); +} + +std::size_t WinToastTemplate::actionsCount() const { + return _actions.size(); +} + +bool WinToastTemplate::hasImage() const { + return _type < WinToastTemplateType::Text01; +} + +bool WinToastTemplate::hasHeroImage() const { + return hasImage() && !_heroImagePath.empty(); +} + +std::vector const& WinToastTemplate::textFields() const { + return _textFields; +} + +std::wstring const& WinToastTemplate::textField(_In_ TextField pos) const { + auto const position = static_cast(pos); + assert(position < _textFields.size()); + return _textFields[position]; +} + +std::wstring const& WinToastTemplate::actionLabel(_In_ std::size_t position) const { + assert(position < _actions.size()); + return _actions[position]; +} + +std::wstring const& WinToastTemplate::imagePath() const { + return _imagePath; +} + +std::wstring const& WinToastTemplate::heroImagePath() const { + return _heroImagePath; +} + +std::wstring const& WinToastTemplate::audioPath() const { + return _audioPath; +} + +std::wstring const& WinToastTemplate::attributionText() const { + return _attributionText; +} + +std::wstring const& WinToastLib::WinToastTemplate::scenario() const { + return _scenario; +} + +INT64 WinToastTemplate::expiration() const { + return _expiration; +} + +WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const { + return _type; +} + +WinToastTemplate::AudioOption WinToastTemplate::audioOption() const { + return _audioOption; +} + +WinToastTemplate::Duration WinToastTemplate::duration() const { + return _duration; +} + +bool WinToastTemplate::isToastGeneric() const { + return hasHeroImage() || _cropHint == WinToastTemplate::Circle; +} + +bool WinToastTemplate::isInlineHeroImage() const { + return _inlineHeroImage; +} + +bool WinToastTemplate::isCropHintCircle() const { + return _cropHint == CropHint::Circle; +} \ No newline at end of file diff --git a/wintoastlib.h b/wintoastlib.h new file mode 100644 index 0000000..fd84e0f --- /dev/null +++ b/wintoastlib.h @@ -0,0 +1,310 @@ +/** + * MIT License + * + * Copyright (C) 2016-2023 WinToast v1.3.0 - Mohammed Boujemaoui + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef WINTOASTLIB_H +#define WINTOASTLIB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::UI::Notifications; +using namespace Windows::Foundation; + +namespace WinToastLib { + + class IWinToastHandler { + public: + enum WinToastDismissalReason { + UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, + ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, + TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut + }; + + virtual ~IWinToastHandler() = default; + virtual void toastActivated() const = 0; + virtual void toastActivated(int actionIndex) const = 0; + virtual void toastDismissed(WinToastDismissalReason state) const = 0; + virtual void toastFailed() const = 0; + }; + + class WinToastTemplate { + public: + enum class Scenario { Default, Alarm, IncomingCall, Reminder }; + enum Duration { System, Short, Long }; + enum AudioOption { Default = 0, Silent, Loop }; + enum TextField { FirstLine = 0, SecondLine, ThirdLine }; + + enum WinToastTemplateType { + ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, + ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, + ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, + ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, + Text01 = ToastTemplateType::ToastTemplateType_ToastText01, + Text02 = ToastTemplateType::ToastTemplateType_ToastText02, + Text03 = ToastTemplateType::ToastTemplateType_ToastText03, + Text04 = ToastTemplateType::ToastTemplateType_ToastText04 + }; + + enum AudioSystemFile { + DefaultSound, + IM, + Mail, + Reminder, + SMS, + Alarm, + Alarm2, + Alarm3, + Alarm4, + Alarm5, + Alarm6, + Alarm7, + Alarm8, + Alarm9, + Alarm10, + Call, + Call1, + Call2, + Call3, + Call4, + Call5, + Call6, + Call7, + Call8, + Call9, + Call10, + }; + + enum CropHint { + Square, + Circle, + }; + + WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); + ~WinToastTemplate(); + + void setFirstLine(_In_ std::wstring const& text); + void setSecondLine(_In_ std::wstring const& text); + void setThirdLine(_In_ std::wstring const& text); + void setTextField(_In_ std::wstring const& txt, _In_ TextField pos); + void setAttributionText(_In_ std::wstring const& attributionText); + void setImagePath(_In_ std::wstring const& imgPath, _In_ CropHint cropHint = CropHint::Square); + void setHeroImagePath(_In_ std::wstring const& imgPath, _In_ bool inlineImage = false); + void setAudioPath(_In_ WinToastTemplate::AudioSystemFile audio); + void setAudioPath(_In_ std::wstring const& audioPath); + void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); + void setDuration(_In_ Duration duration); + void setExpiration(_In_ INT64 millisecondsFromNow); + void setScenario(_In_ Scenario scenario); + void addAction(_In_ std::wstring const& label); + + std::size_t textFieldsCount() const; + std::size_t actionsCount() const; + bool hasImage() const; + bool hasHeroImage() const; + std::vector const& textFields() const; + std::wstring const& textField(_In_ TextField pos) const; + std::wstring const& actionLabel(_In_ std::size_t pos) const; + std::wstring const& imagePath() const; + std::wstring const& heroImagePath() const; + std::wstring const& audioPath() const; + std::wstring const& attributionText() const; + std::wstring const& scenario() const; + INT64 expiration() const; + WinToastTemplateType type() const; + WinToastTemplate::AudioOption audioOption() const; + Duration duration() const; + bool isToastGeneric() const; + bool isInlineHeroImage() const; + bool isCropHintCircle() const; + + private: + std::vector _textFields{}; + std::vector _actions{}; + std::wstring _imagePath{}; + std::wstring _heroImagePath{}; + bool _inlineHeroImage{ false }; + std::wstring _audioPath{}; + std::wstring _attributionText{}; + std::wstring _scenario{ L"Default" }; + INT64 _expiration{ 0 }; + AudioOption _audioOption{ WinToastTemplate::AudioOption::Default }; + WinToastTemplateType _type{ WinToastTemplateType::Text01 }; + Duration _duration{ Duration::System }; + CropHint _cropHint{ CropHint::Square }; + }; + + class WinToast { + public: + enum WinToastError { + NoError = 0, + NotInitialized, + SystemNotSupported, + ShellLinkNotCreated, + InvalidAppUserModelID, + InvalidParameters, + InvalidHandler, + NotDisplayed, + UnknownError + }; + + enum ShortcutResult { + SHORTCUT_UNCHANGED = 0, + SHORTCUT_WAS_CHANGED = 1, + SHORTCUT_WAS_CREATED = 2, + + SHORTCUT_MISSING_PARAMETERS = -1, + SHORTCUT_INCOMPATIBLE_OS = -2, + SHORTCUT_COM_INIT_FAILURE = -3, + SHORTCUT_CREATE_FAILED = -4 + }; + + enum ShortcutPolicy { + /* Don't check, create, or modify a shortcut. */ + SHORTCUT_POLICY_IGNORE = 0, + /* Require a shortcut with matching AUMI, don't create or modify an existing one. */ + SHORTCUT_POLICY_REQUIRE_NO_CREATE = 1, + /* Require a shortcut with matching AUMI, create if missing, modify if not matching. This is the default. */ + SHORTCUT_POLICY_REQUIRE_CREATE = 2, + }; + + WinToast(void); + virtual ~WinToast(); + static WinToast* instance(); + static bool isCompatible(); + static bool isSupportingModernFeatures(); + static bool isWin10AnniversaryOrHigher(); + static std::wstring configureAUMI(_In_ std::wstring const& companyName, _In_ std::wstring const& productName, + _In_ std::wstring const& subProduct = std::wstring(), + _In_ std::wstring const& versionInformation = std::wstring()); + static std::wstring const& strerror(_In_ WinToastError error); + virtual bool initialize(_Out_opt_ WinToastError* error = nullptr); + virtual bool isInitialized() const; + virtual bool hideToast(_In_ INT64 id); + virtual INT64 showToast(_In_ WinToastTemplate const& toast, _In_ IWinToastHandler* eventHandler, + _Out_opt_ WinToastError* error = nullptr); + virtual void clear(); + virtual enum ShortcutResult createShortcut(); + + std::wstring const& appName() const; + std::wstring const& appUserModelId() const; + void setAppUserModelId(_In_ std::wstring const& aumi); + void setAppName(_In_ std::wstring const& appName); + void setShortcutPolicy(_In_ ShortcutPolicy policy); + + protected: + struct NotifyData { + NotifyData() {}; + NotifyData(_In_ ComPtr notify, _In_ EventRegistrationToken activatedToken, + _In_ EventRegistrationToken dismissedToken, _In_ EventRegistrationToken failedToken) : + _notify(notify), _activatedToken(activatedToken), _dismissedToken(dismissedToken), _failedToken(failedToken) {} + + ~NotifyData() { + RemoveTokens(); + } + + void RemoveTokens() { + if (!_readyForDeletion) { + return; + } + + if (_previouslyTokenRemoved) { + return; + } + + if (!_notify.Get()) { + return; + } + + _notify->remove_Activated(_activatedToken); + _notify->remove_Dismissed(_dismissedToken); + _notify->remove_Failed(_failedToken); + _previouslyTokenRemoved = true; + } + + void markAsReadyForDeletion() { + _readyForDeletion = true; + } + + bool isReadyForDeletion() const { + return _readyForDeletion; + } + + IToastNotification* notification() { + return _notify.Get(); + } + + private: + ComPtr _notify{ nullptr }; + EventRegistrationToken _activatedToken{}; + EventRegistrationToken _dismissedToken{}; + EventRegistrationToken _failedToken{}; + bool _readyForDeletion{ false }; + bool _previouslyTokenRemoved{ false }; + }; + + bool _isInitialized{ false }; + bool _hasCoInitialized{ false }; + ShortcutPolicy _shortcutPolicy{ SHORTCUT_POLICY_REQUIRE_CREATE }; + std::wstring _appName{}; + std::wstring _aumi{}; + std::map _buffer{}; + + void markAsReadyForDeletion(_In_ INT64 id); + HRESULT validateShellLinkHelper(_Out_ bool& wasChanged); + HRESULT createShellLinkHelper(); + HRESULT setImageFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isToastGeneric, bool isCropHintCircle); + HRESULT setHeroImageHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, _In_ bool isInlineImage); + HRESULT setBindToastGenericHelper(_In_ IXmlDocument* xml); + HRESULT + setAudioFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& path, + _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); + HRESULT setTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text, _In_ UINT32 pos); + HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& text); + HRESULT addActionHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& action, _In_ std::wstring const& arguments); + HRESULT addDurationHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& duration); + HRESULT addScenarioHelper(_In_ IXmlDocument* xml, _In_ std::wstring const& scenario); + ComPtr notifier(_In_ bool* succeded) const; + void setError(_Out_opt_ WinToastError* error, _In_ WinToastError value); + }; +} // namespace WinToastLib +#endif // WINTOASTLIB_H \ No newline at end of file