E-MailRelay
glogoutput_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file glogoutput_win32.cpp
19///
20
21#include "gdef.h"
22#include "glogoutput.h"
23#include "genvironment.h"
24#include "gfile.h"
25#include <stdexcept>
26#include <fstream>
27
28namespace G
29{
30 namespace LogOutputWindowsImp
31 {
32 std::string thisExe()
33 {
34 // same code is in G::Process:exe()...
35 std::vector<char> buffer ;
36 std::size_t sizes[] = { 80U , 1024U , 32768U , 0U } ; // documented limit of 32k
37 for( std::size_t * size_p = sizes ; *size_p ; ++size_p )
38 {
39 buffer.resize( *size_p+1U , '\0' ) ;
40 DWORD size = static_cast<DWORD>( buffer.size() ) ;
41 HINSTANCE hinstance = HNULL ;
42 DWORD rc = GetModuleFileNameA( hinstance , &buffer[0] , size ) ;
43 if( rc == 0 ) break ;
44 if( rc < size )
45 return std::string( &buffer[0] , rc ) ;
46 }
47 return std::string() ;
48 }
49 std::string basename( std::string s )
50 {
51 std::string::size_type pos1 = s.find_last_of( "\\" ) ;
52 if( pos1 != std::string::npos )
53 s = s.substr( pos1+1U ) ;
54 std::string::size_type pos2 = s.find_last_of( "." ) ;
55 if( pos2 != std::string::npos )
56 s.resize( pos2 ) ;
57 return s ;
58 }
59 bool oldWindows()
60 {
61 static bool old_windows_set = false ;
62 static bool old_windows = false ;
63 if( !old_windows_set )
64 {
65 old_windows_set = true ;
66 old_windows = !IsWindowsVistaOrGreater() ;
67 }
68 return old_windows ;
69 }
70 }
71}
72
73void G::LogOutput::osoutput( int fd , G::Log::Severity severity , char * message , std::size_t n )
74{
75 // event log
76 //
77 if( m_config.m_use_syslog &&
78 severity != Log::Severity::Debug &&
79 severity != Log::Severity::InfoVerbose &&
80 m_handle != HNULL )
81 {
82 DWORD id = 0x400003E9L ; // 1001
83 WORD type = EVENTLOG_INFORMATION_TYPE ;
84 if( severity == Log::Severity::Warning )
85 {
86 id = 0x800003EAL ; // 1002
87 type = EVENTLOG_WARNING_TYPE ;
88 }
89 else if( severity == Log::Severity::Error || severity == Log::Severity::Assertion )
90 {
91 id = 0xC00003EBL ; // 1003
92 type = EVENTLOG_ERROR_TYPE ;
93 }
94
95 // very old windowses do not seem to recognise "!S!" format specifiers so
96 // as a workround (fwiw) use additional entries in messages.mc (1011, etc)
97 if( LogOutputWindowsImp::oldWindows() )
98 id += 10 ;
99
100 message[n] = '\0' ;
101 const char * p[] = { message , nullptr } ;
102 BOOL rc = ReportEventA( m_handle , type , 0 , id , nullptr , 1 , 0 , p , nullptr ) ;
103 GDEF_IGNORE_VARIABLE( rc ) ;
104 }
105
106 // standard error or log file -- note that stderr is not accessible if a gui
107 // build -- stderr will be text mode whereas a log file will be binary
108 //
109 if( fd > 2 ) message[n++] = '\r' ;
110 message[n++] = '\n' ;
111 G::File::write( fd , message , n ) ;
112}
113
114void G::LogOutput::osinit()
115{
116 if( m_config.m_use_syslog )
117 {
118 std::string this_exe = LogOutputWindowsImp::thisExe() ;
119 if( !this_exe.empty() )
120 {
121 std::string this_name = LogOutputWindowsImp::basename( this_exe ) ;
122 G::LogOutput::register_( this_exe ) ;
123 m_handle = RegisterEventSourceA( nullptr , this_name.c_str() ) ;
124 if( m_handle == HNULL && !m_config.m_allow_bad_syslog )
125 throw EventLogError() ;
126 }
127 }
128}
129
130void G::LogOutput::register_( const std::string & exe_path )
131{
132 // this method will normally fail because of access rights so it
133 // should also be run as part of the install process
134
135 std::string reg_path =
136 "SYSTEM\\CurrentControlSet\\services\\eventlog\\Application\\" +
137 LogOutputWindowsImp::basename(exe_path) ;
138
139 HKEY key = 0 ;
140 int sam = KEY_WRITE ;
141 LONG e = RegCreateKeyExA( HKEY_LOCAL_MACHINE , reg_path.c_str() , 0 , NULL , 0 , sam , NULL , &key , NULL ) ;
142 if( e == ERROR_SUCCESS && key != 0 )
143 {
144 DWORD one = 1 ;
145 DWORD types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE ;
146 RegSetValueExA( key , "EventMessageFile" , 0 , REG_SZ ,
147 reinterpret_cast<const BYTE*>(exe_path.c_str()) ,
148 static_cast<DWORD>(exe_path.length())+1U ) ;
149 RegSetValueExA( key , "CategoryCount" , 0 , REG_DWORD ,
150 reinterpret_cast<const BYTE*>(&one) , sizeof(one) ) ;
151 RegSetValueExA( key , "CategoryMessageFile" , 0 , REG_SZ ,
152 reinterpret_cast<const BYTE*>(exe_path.c_str()) ,
153 static_cast<DWORD>(exe_path.length())+1U ) ;
154 RegSetValueExA( key , "TypesSupported" , 0 , REG_DWORD ,
155 reinterpret_cast<const BYTE*>(&types) , sizeof(types) ) ;
156 }
157 if( key != 0 )
158 RegCloseKey( key ) ;
159}
160
161void G::LogOutput::oscleanup() const noexcept
162{
163 if( m_handle != HNULL )
164 DeregisterEventSource( m_handle ) ;
165}
166
static ssize_t write(int fd, const char *, std::size_t) noexcept
Calls ::write() or equivalent.
Definition: gfile_unix.cpp:144
static void register_(const std::string &exe)
Registers the given executable as a source of logging.
Low-level classes.
Definition: garg.h:30