Coverage Report

Created: 2025-10-28 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pistache/src/common/pist_syslog.cc
Line
Count
Source
1
/*
2
 * SPDX-FileCopyrightText: 2024 Duncan Greatwood
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5
 */
6
7
/******************************************************************************
8
 * pist_syslog.cpp
9
 *
10
 * Logging Facilities
11
 *
12
 */
13
14
#include <mutex>
15
16
#include <pistache/winornix.h>
17
#include <pistache/pist_quote.h>
18
#include <pistache/ps_strl.h>
19
20
#include <pistache/ps_basename.h> // for PS_BASENAME_R
21
22
#include <stdio.h> // snprintf
23
#include <stdlib.h> // malloc
24
25
#include PST_CLOCK_GETTIME_HDR
26
27
#include <time.h>
28
#include <string.h>
29
#include <thread>
30
31
#ifdef _IS_WINDOWS
32
#include <string>
33
#include <iostream>
34
#include <codecvt>
35
#include <locale>
36
#endif
37
38
#include <cctype> // std::ispunct
39
#include <algorithm> // std::remove_copy_if
40
#include <vector>
41
// #include <limits.h> // PATH_MAX
42
#include PST_MAXPATH_HDR
43
44
#ifdef __APPLE__
45
#include <mach-o/dyld.h> // _NSGetExecutablePath
46
#endif
47
48
#ifdef __APPLE__
49
#include <Availability.h>
50
  // Apple's unified logging system launched in macOS Sierra (macOS 10.12) and
51
  // iOS 10. https://developer.apple.com/documentation/os/logging
52
  // The unified logging system was successor to Apple System Logger, which
53
  // itself replaced the use of syslog
54
  //
55
  // If viewing log in console.app, from console.app menu bar can do Action ->
56
  // Include Info Messages and Action -> Include Debug Messages.
57
  // Note that you have to press "start" in console.app to make it start
58
  // capturing logs.
59
  // In Console.app search, s;com.github.pistacheio.pistache [Enter] to filter
60
  // on our pistache subsysem
61
  //
62
  // sudo log stream --style compact --debug --info --predicate 'subsystem="com.github.pistacheio.pistache"'
63
  //
64
  // From the command line, try:
65
  //   sudo log stream --style compact --debug --info --predicate 'subsystem="com.github.pistacheio.pistache"'
66
  // ("log show" looks at existing logs, "stream" streams new log messages)
67
  // For stream, you can pipe to a file of your choice to retain of course
68
  // "man log" for more info
69
70
  #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
71
    #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1012
72
      #if __has_builtin(__builtin_os_log_format)
73
        // Homebrew gcc 14.2.0 doesn't appear to have __builtin_os_log_format
74
        // which prevents us from using os/log.h (Nov/2024). Note: gcc 10
75
        // supports __has_builtin.
76
        #define PIST_USE_OS_LOG 1
77
      #endif
78
    #endif
79
  #endif
80
#endif
81
82
#ifdef PIST_USE_OS_LOG
83
  #include <os/log.h>
84
#elif defined _IS_WINDOWS
85
  #include <windows.h> // needed for PST_THREAD_HDR (processthreadsapi.h)
86
  #include <stringapiset.h>
87
  #include <evntprov.h>
88
89
  #ifdef __MINGW32__
90
    #ifndef _Pre_cap_
91
      // With Visual Studio, _Pre_cap_ is a multi-layer macro defined in sal.h,
92
      // the source-code annotation language used by Microsoft; and it is used
93
      // in the generated header file pist_winlog.h. For mingw, _Pre_cap_
94
      // doesn't seem to be defined in their sal.h, and we simply define it to
95
      // be nothing so pist_winlog.h can compile.
96
      #include <sal.h>
97
      #ifndef _Pre_cap_
98
        #define _Pre_cap_(__T)
99
      #endif
100
    #endif
101
  #endif
102
103
  #ifdef __MINGW32__
104
    #pragma GCC diagnostic push
105
    #pragma GCC diagnostic ignored "-Wunknown-pragmas"
106
    // The generated header file pist_winlog.h likely includes some Visual
107
    // Studio specific pragmas such as "#pragma warning(suppress:
108
    // 26451)". gcc/mingw would normally generate a warning for pragmas it
109
    // doesn't recognize; here we temporarily hide such warnings while
110
    // pist_winlog.h is compiled.
111
  #endif
112
  #include "pist_winlog.h" // generated header file
113
  #ifdef __MINGW32__
114
    #pragma GCC diagnostic pop
115
116
    // pist_winlog.h declares this ULONG as an extern, but does not provide an
117
    // instantiation for it. WIth mingw, we have to provide an instantiation to
118
    // avoid an "undefined reference" linker error
119
    ULONG Pistache_ProviderEnableBits[1] = {0};
120
  #endif
121
122
  // In Windows, Pistache uses the ETW ("Event Tracing") system for logging.
123
  //   https://learn.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
124
  // ETW events are defined in the pist_winlog.man manifest file, from which is
125
  // generated the C header file pist_winlog.h, used here.
126
  //
127
  // For events above level DEBUG, the event is sent to the built-in
128
  // Application channel. This make events appear automatically in Event
129
  // Viewer, even if you don't run logman.
130
  //
131
  // DEBUG events (in debug builds) are sent to our custom Pistache debug
132
  // channel. Verbose/debug event streams are not to be sent to the Application
133
  // channel, because they will clog up the channel.
134
  //
135
  // Since DEBUG events are not consumed automatically by EventViewer via the
136
  // Application channel, we will need another event consumer to record the
137
  // events. We can use the Windows utility logman.exe.
138
  //
139
  // To use logman, you can do:
140
  //   logman start -ets Pistache -p "Pistache-Provider" 0 0 -o pistache.etl
141
  //  Then run your program, which you want to log
142
  //  Once your program is complete, you can do:
143
  //   logman stop Pistache -ets
144
  // This causes the log information to be written out to pistache.etl
145
  //
146
  // You can view the etl file:
147
  //   1/ Use Event Viewer. (In Event Viewer, Action -> Open Saved Log, then
148
  //      choose pistache.etl).
149
  //   2/ Convert the etl to XML:
150
  //        tracerpt -y pistache.etl
151
  //        Then view dumpfile.xml in a text editor or XML viewer
152
  // Alternatively, you can have logman generate a CSV file instead of an .etl
153
  // by adding the "-f csv" option to the "logman start..." command.
154
  //
155
  // logman has many other options, do "logman -?" to see the list.
156
  //
157
  // To use logging, you must also:
158
  //  1/ First, copy pistachelog.dll to the predefined location
159
  //     "$env:ProgramFiles\pistache_distribution\bin"
160
  //  2/ Install the Pistache manifest file by doing:
161
  //     wevtutil im "pist_winlog.man"
162
  // src/meson.build should do both 1/ and 2/ automatically upon "meson build"
163
164
#else
165
  #include <syslog.h>
166
#endif
167
168
#include <stdarg.h>
169
#include <string.h> // for strcat
170
171
#include PST_THREAD_HDR // for pthread_self (getting thread ID)
172
173
#include PST_MAXPATH_HDR
174
175
#include <sys/types.h> // for getpid()
176
#include PST_MISC_IO_HDR // unistd.h e.g. close
177
178
#include "pistache/pist_syslog.h"
179
#include <memory> // for std::shared_ptr
180
181
182
/*****************************************************************************/
183
#ifdef _IS_WINDOWS
184
185
#include <sstream> // std::wostringstream
186
#include <winreg.h> // Registry functions
187
#include <atomic>
188
189
class LogToStdPutAsWell
190
{
191
public:
192
    LogToStdPutAsWell() :
193
        logToStdoutAsWell(-1),
194
        pistacheHkey(0),
195
        ltsawMonitorThreadNextToUseIdx(0)
196
    {}
197
198
    ~LogToStdPutAsWell()
199
        {
200
            for(unsigned int i=0; i<2; ++i)
201
            {
202
                if (logToStdoutAsWellMonitorThread[i])
203
                {
204
                    // If we allow std::~thread to be called while thread is
205
                    // still joinable (i.e. before it is already joined),
206
                    // std::termimate will be called immediately
207
                    if (logToStdoutAsWellMonitorThread[i]->joinable())
208
                        logToStdoutAsWellMonitorThread[i]->join();
209
                }
210
            }
211
        }
212
public:
213
    // Don't access directly, use getLogToStdoutAsWell()
214
    std::atomic_int logToStdoutAsWell; // -1 uninited, 0 false, 1 true
215
    std::mutex logToStdoutAsWellMutex;
216
    HKEY pistacheHkey;
217
    std::unique_ptr<std::thread> logToStdoutAsWellMonitorThread[2];
218
    unsigned int ltsawMonitorThreadNextToUseIdx;
219
    // We use the logToStdoutAsWellMonitorThread alternately - when one
220
    // monitor thread is running, and it needs to create a new monitor thread,
221
    // it uses the other logToStdoutAsWellMonitorThread for the new
222
    // std::thread unique_ptr.
223
};
224
static LogToStdPutAsWell lLogToStdOutAsWellInst;
225
226
227
// getAndInitPsLogToStdoutAsWellPrv helper - on a failure, we try and send an
228
// Alert to the Windows Application logging channel, and also to stderr - in
229
// the hope someone will notice why logging is not doing as expected.
230
static void getPsLogToStdoutAsWellLogFailPrv(const wchar_t * msg_prefix,
231
                                             LSTATUS err_code)
232
{
233
    std::wostringstream err_wostringstream;
234
    err_wostringstream << L"getPsLogToStdoutAsWell: "
235
        << msg_prefix << L", error code " << err_code;
236
237
    TCHAR err_msg_buff_tch[2048+16];
238
    DWORD err_msg_res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
239
                                      nullptr, // no msg source string
240
                                      err_code,
241
                                      0, // unspecified language
242
                                      &(err_msg_buff_tch[0]),
243
                                      2048, // buff size
244
                                      nullptr); // msg arguments
245
    if (err_msg_res == 0)
246
    {
247
        err_wostringstream << L". <FormatMessage Failed>.";
248
    }
249
    else
250
    {
251
        err_wostringstream << L", " << &(err_msg_buff_tch[0]);
252
    }
253
254
    std::wstring msg_w(err_wostringstream.str());
255
256
    EventWritePSTCH_CBLTIN_ALERT_NL_AssumeEnabled(msg_w.c_str());
257
258
    char msg_buf_chs[2048+16];
259
    int wctmb_res = WideCharToMultiByte(CP_UTF8,
260
                                        0, // no conversion flags, use defaults
261
                                        msg_w.c_str(),
262
                                        -1, // msg_w is null-terminated
263
                                        &(msg_buf_chs[0]),
264
                                        2048,
265
                                        nullptr, // ptr to ch for invalid char
266
                                        nullptr); // any invalid char?
267
    if (wctmb_res <= 0)
268
    {
269
        EventWritePSTCH_CBLTIN_ALERT_NL_AssumeEnabled(
270
            L"getPsLogToStdoutAsWell: WideCharToMultiByte failure for stderr");
271
        std::cerr <<
272
            "getPsLogToStdoutAsWell: WideCharToMultiByte failure for stderr"
273
                  << std::endl;
274
        return;
275
    }
276
277
    std::cerr << (&(msg_buf_chs[0])) << std::endl;
278
}
279
280
281
// Reads "HKCU:\Software\pistacheio\pistache\psLogToStdoutAsWell" property from
282
// the Windows registry, and returns its value, either 0, 1 or 10. Any Registry
283
// key property value other than 0, 1 or 10 causes 1 to be returned. If the key
284
// doesn't exist or can't be read, 0 is returned.
285
//
286
// If the property does not exist in the registry yet, we create it here, set
287
// it to zero, and return 0.
288
//
289
// logToStdoutAsWellMutex locked before this is called
290
// logToStdoutAsWell value is NOT set by this function
291
static DWORD getAndInitPsLogToStdoutAsWellPrv()
292
{
293
    HKEY hklm_software_key = 0;
294
    LSTATUS open_res = RegOpenKeyExA(
295
        HKEY_CURRENT_USER,
296
        "Software",
297
        0, // options (not symbolic link)
298
        KEY_READ,
299
        &hklm_software_key);
300
    if (open_res != ERROR_SUCCESS)
301
    {
302
        getPsLogToStdoutAsWellLogFailPrv(
303
            L"Failed to open registry key HKCU:\\Software", open_res);
304
        return(0);
305
    }
306
307
    if (lLogToStdOutAsWellInst.pistacheHkey)
308
    {
309
        RegCloseKey(lLogToStdOutAsWellInst.pistacheHkey);
310
        lLogToStdOutAsWellInst.pistacheHkey = 0;
311
    }
312
313
    DWORD dw_disposition = 0;
314
    LSTATUS create_res = RegCreateKeyExA(
315
        hklm_software_key,
316
        "pistacheio\\pistache", // will create both "pistacheio" and
317
                                          // "pistache" if needed
318
        0,
319
        nullptr, // lpClass
320
        0, // default flags
321
        KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_NOTIFY, // reqd rights
322
        nullptr, // don't allow child process to inherit
323
        &lLogToStdOutAsWellInst.pistacheHkey, // HKEY result
324
        &dw_disposition);
325
    // Note: if the key already exists, RegCreateKeyExA opens it
326
327
    if (create_res != ERROR_SUCCESS)
328
    {
329
        getPsLogToStdoutAsWellLogFailPrv(
330
            L"Failed to create/open registry key "
331
             "HKCU:\\Software\\pistacheio\\pistache", create_res);
332
        return(0);
333
    }
334
335
    #ifdef DEBUG
336
    const std::wstring disposition_msg(
337
        (dw_disposition == REG_OPENED_EXISTING_KEY) ?
338
        L"Opened existing registry key HKCU:\\Software\\pistacheio\\pistache" :
339
        ((dw_disposition == REG_CREATED_NEW_KEY) ?
340
         L"Created new registry key HKCU:\\Software\\pistacheio\\pistache" :
341
         L"Unknown RegCreateKeyExA disposition"));
342
    EventWritePSTCH_DEBUG_NL(disposition_msg.c_str());
343
    #endif
344
345
    if (dw_disposition != REG_CREATED_NEW_KEY)
346
    {
347
        uint64_t val = 0; // only needs to be DWORD, but just in case
348
        DWORD val_size = sizeof(DWORD);
349
        const LSTATUS get_val_res =
350
            RegGetValueA(lLogToStdOutAsWellInst.pistacheHkey,
351
                         nullptr, // subkey - none
352
                         "psLogToStdoutAsWell", // val name
353
                         RRF_RT_REG_DWORD, // type REG_DWORD
354
                         nullptr, // out type
355
                         &val, // out data
356
                         &val_size);  // out data size
357
        if (get_val_res == ERROR_SUCCESS)
358
        {
359
            if ((val != 10) && (val != 0) && (val != 1))
360
                return(1);
361
362
            return(static_cast<DWORD>(val));
363
        }
364
365
        if (get_val_res != ERROR_FILE_NOT_FOUND)
366
        {
367
            getPsLogToStdoutAsWellLogFailPrv(
368
                L"Failed to get Registry value psLogToStdoutAsWell",
369
                get_val_res);
370
            return(0);
371
        }
372
    }
373
374
    // value didn't exist
375
376
    BYTE data_to_set[8] = {0}; // bigger than it needs to be
377
    LSTATUS set_val_res = RegSetValueExA(lLogToStdOutAsWellInst.pistacheHkey,
378
                                         "psLogToStdoutAsWell", // val name
379
                                         0, // Reserved
380
                                         REG_DWORD, // type to set
381
                                         &(data_to_set[0]), // data content
382
                                         sizeof(DWORD)); // data size
383
384
    if (set_val_res != ERROR_SUCCESS)
385
    {
386
        getPsLogToStdoutAsWellLogFailPrv(
387
            L"Failed to set Registry value psLogToStdoutAsWell", set_val_res);
388
        return(0);
389
    }
390
391
    return(0);
392
}
393
394
// Calls getAndInitPsLogToStdoutAsWellPrv,and then sets up change monitoring
395
// for the key
396
// logToStdoutAsWellMutex locked before this is called
397
// logToStdoutAsWell value IS set by this function
398
static DWORD getLogToStdoutAsWellAndMonitorPrv()
399
{
400
    DWORD log_to_stdout_as_well = getAndInitPsLogToStdoutAsWellPrv();
401
    if (!lLogToStdOutAsWellInst.pistacheHkey)
402
    { // Can't monitor without lLogToStdOutAsWellInst.pistacheHkey
403
        lLogToStdOutAsWellInst.logToStdoutAsWell = log_to_stdout_as_well;
404
        return(log_to_stdout_as_well);
405
    }
406
407
    bool we_set_l_log_to_stdout_as_well = false;
408
409
    HANDLE h_event= CreateEventA(nullptr, // handle not inherited by child
410
                                 FALSE,// auto-set to non-signaled after signal
411
                                 FALSE,// initial state is non-signaled
412
                                 nullptr);// Null = event name
413
    if (h_event == nullptr)
414
    {
415
        DWORD err_code = GetLastError();
416
        getPsLogToStdoutAsWellLogFailPrv(
417
            L"CreateEvent fail, cannot monitor psLogToStdoutAsWell Registry "
418
             "value",
419
            err_code);
420
    }
421
    else
422
    {
423
        LSTATUS reg_notify_res = RegNotifyChangeKeyValue(
424
            lLogToStdOutAsWellInst.pistacheHkey,
425
            false, // report changes in the key only, not subkeys
426
            REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET
427
            | REG_NOTIFY_CHANGE_SECURITY
428
            | REG_NOTIFY_THREAD_AGNOSTIC, // Changes that will be reported
429
            h_event, // event to be signalled on reportable change
430
            true); // true => RegNotifyChangeKeyValue to be asynchronous
431
432
        if (reg_notify_res == ERROR_SUCCESS)
433
        {
434
            lLogToStdOutAsWellInst.logToStdoutAsWell = log_to_stdout_as_well;
435
            we_set_l_log_to_stdout_as_well = true;
436
437
            unsigned int thread_to_use_idx =
438
                lLogToStdOutAsWellInst.ltsawMonitorThreadNextToUseIdx;
439
            lLogToStdOutAsWellInst.ltsawMonitorThreadNextToUseIdx =
440
                                                 ((thread_to_use_idx + 1) % 2);
441
442
            if (lLogToStdOutAsWellInst.
443
                             logToStdoutAsWellMonitorThread[thread_to_use_idx])
444
            {
445
                // Wait for the thread to exit BEFORE we delete the std::thread
446
                // instance
447
                lLogToStdOutAsWellInst.
448
                     logToStdoutAsWellMonitorThread[thread_to_use_idx]->join();
449
450
                // Effectively, delete the std::thread instance
451
                lLogToStdOutAsWellInst.
452
                   logToStdoutAsWellMonitorThread[thread_to_use_idx] = nullptr;
453
            }
454
455
            lLogToStdOutAsWellInst.
456
                logToStdoutAsWellMonitorThread[thread_to_use_idx] =
457
                std::make_unique<std::thread> ([h_event]
458
                 {
459
                     auto wfso_res = WaitForSingleObject(h_event, INFINITE);
460
461
                     std::lock_guard<std::mutex>
462
                         guard(lLogToStdOutAsWellInst.logToStdoutAsWellMutex);
463
464
                     if (wfso_res  == WAIT_FAILED)
465
                     {
466
                         DWORD err_code = GetLastError();
467
                         getPsLogToStdoutAsWellLogFailPrv(
468
                             L"WaitForSingleObject fail, cannot monitor "
469
                              "psLogToStdoutAsWell Registry value",
470
                             err_code);
471
                     }
472
                     else
473
                     { // A change has occured
474
                         // Note - the monitoring only lasts for one change
475
                         // event, so we monitor it again
476
                         DWORD log_to_stdout_as_well =
477
                             getLogToStdoutAsWellAndMonitorPrv();
478
                         lLogToStdOutAsWellInst.logToStdoutAsWell =
479
                                                         log_to_stdout_as_well;
480
                     }
481
482
                     CloseHandle(h_event);
483
                 });
484
        }
485
        else
486
        {
487
            getPsLogToStdoutAsWellLogFailPrv(
488
                L"RegNotifyChangeKeyValue fail, cannot monitor "
489
                 "psLogToStdoutAsWell Registry value",
490
                reg_notify_res);
491
        }
492
    }
493
494
    if (!we_set_l_log_to_stdout_as_well)
495
        lLogToStdOutAsWellInst.logToStdoutAsWell = log_to_stdout_as_well;
496
    return(log_to_stdout_as_well);
497
}
498
499
500
static DWORD getLogToStdoutAsWell()
501
{
502
    { // encapsulate
503
        int my_log_to_stdout_as_well(lLogToStdOutAsWellInst.logToStdoutAsWell);
504
        if (my_log_to_stdout_as_well >= 0)
505
            return(my_log_to_stdout_as_well > 0);
506
    }
507
508
    std::lock_guard<std::mutex> guard(
509
                                lLogToStdOutAsWellInst.logToStdoutAsWellMutex);
510
    if (lLogToStdOutAsWellInst.logToStdoutAsWell >= 0)
511
        return(lLogToStdOutAsWellInst.logToStdoutAsWell > 0);
512
513
    return(getLogToStdoutAsWellAndMonitorPrv());
514
}
515
516
#endif // of ifdef _IS_WINDOWS
517
518
/*****************************************************************************/
519
520
521
class PSLogging
522
{
523
public:
524
    PSLogging();
525
    ~PSLogging();
526
527
public:
528
    static std::shared_ptr<PSLogging> getPSLogging();
529
530
public:
531
    void log(int _priority, bool _andPrintf,
532
             const char * _format, va_list _ap);
533
    void log(int _priority, bool _andPrintf,
534
             const char * _str);
535
};
536
537
// For use of class PSLogging only; treat as private to PSLogging
538
static std::mutex lPSLoggingSingletonMutex;
539
static std::shared_ptr<PSLogging> lPSLoggingSingleton;
540
541
/*****************************************************************************/
542
543
std::shared_ptr<PSLogging> PSLogging::getPSLogging()
544
0
{
545
0
    if (!lPSLoggingSingleton)
546
0
    {
547
0
        std::lock_guard<std::mutex> lock(lPSLoggingSingletonMutex);
548
0
        if (!lPSLoggingSingleton)
549
0
            lPSLoggingSingleton = std::make_shared<PSLogging>();
550
0
    }
551
0
    return(lPSLoggingSingleton);
552
0
}
553
554
// ---------------------------------------------------------------------------
555
556
#ifdef PIST_USE_OS_LOG
557
static os_log_t pist_os_log_ref = OS_LOG_DEFAULT;
558
#endif
559
560
static const char * gLogEntryPrefix = "PSTCH";
561
static char gIdentBuff[PST_MAXPATHLEN + 6] = {0};
562
static bool gSetPsLogCategoryCalledWithNull = false;
563
564
static bool my_is_punct(std::string::value_type & ch)
565
0
{ // There's probably a way to do with with std::function
566
0
    return(static_cast<bool>(std::ispunct(static_cast<int>(ch))));
567
0
}
568
569
570
static std::string getLogIdent()
571
0
{
572
0
    char prog_path[PST_MAXPATHLEN+6];
573
0
    prog_path[0] = 0;
574
575
    #ifdef __APPLE__
576
    uint32_t bufsize = PST_MAXPATHLEN;
577
    if (_NSGetExecutablePath(&(prog_path[0]), &bufsize) != 0)
578
        return(std::string());
579
    #elif defined _IS_WINDOWS
580
    if (GetModuleFileNameA(nullptr, // NULL->executable file of current process
581
                           &(prog_path[0]), PST_MAXPATHLEN) == 0)
582
        return(std::string()); // GetModuleFileNameA returns strlen on success
583
    #else
584
0
    if (readlink("/proc/self/exe", &(prog_path[0]), PST_MAXPATHLEN) == -1)
585
0
        return(std::string());
586
0
    #endif
587
588
0
    const size_t prog_path_len = strlen(&(prog_path[0]));
589
0
    if (!prog_path_len)
590
0
        return(std::string());
591
592
0
    std::vector<char> bname_buff(
593
0
        std::max<size_t>(PST_MAXPATHLEN+16, prog_path_len+16));
594
0
    bname_buff[0] = 0;
595
596
0
    char * prog_name = PS_BASENAME_R(&(prog_path[0]), bname_buff.data());
597
0
    if ((!prog_name) || (!strlen(prog_name)))
598
0
        prog_name = &(prog_path[0]);
599
600
0
    if (strlen(prog_name) <= 5)
601
0
        return(std::string(prog_name));
602
603
0
    std::string prog_name_raw(prog_name);
604
0
    std::string prog_name_no_punct;
605
0
    std::remove_copy_if(prog_name_raw.begin(), prog_name_raw.end(),
606
0
          std::back_inserter(prog_name_no_punct), my_is_punct);
607
608
0
    if ((prog_name_no_punct.size() >= 3) && (prog_name_no_punct.size() <= 5))
609
0
        return(prog_name_no_punct);
610
611
0
    std::string & prog_name_to_sample((prog_name_no_punct.size() > 5) ?
612
0
                                      prog_name_no_punct : prog_name_raw);
613
614
    // Take the middle 5 chars
615
0
    std::string res(prog_name_to_sample.substr(prog_name_to_sample.size()/2 -3,
616
0
                                               5));
617
0
    return(res);
618
0
}
619
620
621
622
623
624
625
PSLogging::PSLogging()
626
0
{
627
0
    if (!gIdentBuff[0])
628
0
    {
629
0
        std::string log_ident(getLogIdent());
630
0
        PS_STRLCPY(&(gIdentBuff[0]), log_ident.empty() ?
631
0
                                           gLogEntryPrefix : log_ident.c_str(),
632
0
                   PST_MAXPATHLEN);
633
0
    }
634
635
    #ifdef PIST_USE_OS_LOG
636
637
    // Instead of syslog, on apple should likely be using os_log (and possibly
638
    // os_log_create). Note BOTH "man 3 os_log" the (logging call) and "man 5
639
    // os_log" configuration.
640
641
    pist_os_log_ref = os_log_create("com.github.pistacheio.pistache",
642
                                    &(gIdentBuff[0]));
643
    #elif defined _IS_WINDOWS
644
645
    #ifdef DEBUG
646
    ULONG reg_res =
647
    #endif
648
        EventRegisterPistache_Provider(); // macro calls EventRegister
649
    #ifdef DEBUG
650
    if (reg_res != ERROR_SUCCESS)
651
        throw std::runtime_error("Windows logging EventRegister failed");
652
    #endif
653
654
    const wchar_t * pist_start_wmsg =
655
        L"Pistache start. INFO and up log messages visible in Event Viewer."
656
        #ifdef DEBUG
657
         " See pist_syslog.cc comments to view DEBUG and up logging."
658
        #endif
659
        ;
660
    EventWritePSTCH_CBLTIN_INFO_NL_AssumeEnabled(pist_start_wmsg);
661
662
    #else
663
664
0
    if (!gSetPsLogCategoryCalledWithNull)
665
0
    {
666
667
0
        int log_opts = (LOG_NDELAY | LOG_PID);
668
        #ifdef DEBUG
669
        log_opts |= LOG_CONS; // send to console if syslog not working
670
        // OR with LOG_PERROR to send every log message to stdout
671
        #endif
672
673
0
        openlog(&(gIdentBuff[0]), log_opts, LOG_USER);
674
0
    }
675
676
0
    #endif // of not ifdef PIST_USE_OS_LOG
677
0
}
678
679
PSLogging::~PSLogging()
680
0
{
681
    #ifdef _IS_WINDOWS
682
    EventWritePSTCH_CBLTIN_INFO_NL_AssumeEnabled(L"Pistache exiting");
683
    EventUnregisterPistache_Provider(); // macro calls EventUnregister
684
    #else
685
0
    if (!gSetPsLogCategoryCalledWithNull)
686
0
        closelog();
687
0
    #endif
688
0
}
689
690
// ---------------------------------------------------------------------------
691
692
// rets -1 for fail, 0 for OK
693
static int snprintProcessAndThread(char * _buff, size_t _buffSize)
694
0
{
695
0
    if (!_buff)
696
0
        return(-1);
697
0
    if (!_buffSize)
698
0
        return(-1);
699
700
0
    PST_THREAD_ID pt = PST_THREAD_ID_SELF(); // This function always succeeds
701
702
0
    unsigned char *ptc =
703
0
        reinterpret_cast<unsigned char*>((reinterpret_cast<void*>(&pt)));
704
0
    int buff_would_have_been_len = 0;
705
0
    _buff[0] = 0;
706
707
    // Skip leading 0s to make str shorter (but don't reduce to zero len)
708
0
    size_t size_pt = sizeof(pt);
709
0
    unsigned int ch_to_skip = 0;
710
0
    for(;(ch_to_skip+1) < size_pt; ++ch_to_skip)
711
0
    {
712
0
        if (ptc[ch_to_skip])
713
0
            break;
714
0
    }
715
0
    ptc += ch_to_skip;
716
0
    size_pt -= ch_to_skip;
717
718
    // Skip trailing 0s to make str shorter (but don't reduce to zero len)
719
0
    for(; size_pt>1; --size_pt)
720
0
    {
721
0
        if (ptc[size_pt-1])
722
0
            break;
723
0
    }
724
725
    // little endian, if it matters
726
0
    for (int i=((static_cast<int>(size_pt))-1); i>=0; --i)
727
0
    {
728
0
        buff_would_have_been_len =
729
0
            snprintf(_buff, _buffSize, "%02x", static_cast<unsigned>(ptc[i]));
730
0
        if (buff_would_have_been_len <= 0)
731
0
        {
732
0
            _buff[0] = 0;
733
0
            return(-1);
734
0
        }
735
0
        if ((static_cast<unsigned int>(buff_would_have_been_len)) >= _buffSize)
736
0
            return(-1); // output truncated, see snprintf man page
737
0
        _buff += buff_would_have_been_len;
738
0
        _buffSize -= buff_would_have_been_len;
739
0
    }
740
741
0
    return(0);
742
0
}
743
744
#ifdef PIST_USE_OS_LOG
745
746
#define OS_LOG_BY_PRIORITY_FORMAT_ARG(POL_FORM, POL_ARG)                \
747
    switch(_priority)                                                   \
748
    {                                                                   \
749
    case LOG_EMERG:                                                     \
750
    case LOG_ALERT:                                                     \
751
    case LOG_CRIT:                                                      \
752
        os_log_fault(pist_os_log_ref, POL_FORM, POL_ARG);               \
753
        break;                                                          \
754
                                                                        \
755
    case LOG_ERR:                                                       \
756
        os_log_error(pist_os_log_ref, POL_FORM, POL_ARG);               \
757
        break;                                                          \
758
                                                                        \
759
    case LOG_WARNING:                                                   \
760
        os_log(pist_os_log_ref, POL_FORM, POL_ARG);                     \
761
        break;                                                          \
762
                                                                        \
763
    case LOG_NOTICE:                                                    \
764
    case LOG_INFO:                                                      \
765
        os_log_info(pist_os_log_ref, POL_FORM, POL_ARG);                \
766
        break;                                                          \
767
                                                                        \
768
    case LOG_DEBUG:                                                     \
769
        os_log_debug(pist_os_log_ref, POL_FORM, POL_ARG);               \
770
        break;                                                          \
771
                                                                        \
772
    default:                                                            \
773
        os_log_fault(pist_os_log_ref, "Bad log priority %d",_priority); \
774
        os_log_fault(pist_os_log_ref, POL_FORM, POL_ARG);               \
775
        break;                                                          \
776
    }
777
778
#define OS_LOG_BY_PRIORITY OS_LOG_BY_PRIORITY_FORMAT_ARG("%s", &(buff[0]))
779
780
#elif defined _IS_WINDOWS
781
782
// POL_ARG is const char *
783
#define WIN_LOG_BY_PRIORITY_CSTR(POL_ARG)                         \
784
{                                                                       \
785
    std::wstring dummy_buff_as_wstr(L"MultiByteToWideChar Fail");       \
786
    std::wstring buff_as_wstr;                                          \
787
    const wchar_t * buff_as_wstr_data = nullptr;                           \
788
                                                                        \
789
    int convert_result = MultiByteToWideChar(CP_UTF8, 0, POL_ARG,       \
790
                           static_cast<int>(strlen(POL_ARG)), nullptr, 0); \
791
    if (convert_result <= 0)                                            \
792
    {                                                                   \
793
        buff_as_wstr_data = dummy_buff_as_wstr.data();                  \
794
    }                                                                   \
795
    else                                                                \
796
    {                                                                   \
797
        buff_as_wstr.resize(convert_result+10);                         \
798
        convert_result = MultiByteToWideChar(CP_UTF8, 0, POL_ARG,       \
799
                   static_cast<int>(strlen(POL_ARG)), &buff_as_wstr[0], \
800
                   static_cast<int>(buff_as_wstr.size())); \
801
        buff_as_wstr_data = (convert_result <= 0) ?                     \
802
            dummy_buff_as_wstr.data() : buff_as_wstr.data();            \
803
    }                                                                   \
804
                                                                        \
805
    switch(_priority)                                                   \
806
    {                                                                   \
807
    case LOG_EMERG:                                                     \
808
        EventWritePSTCH_CBLTIN_EMERG_NL_AssumeEnabled(buff_as_wstr_data); \
809
        break;                                                          \
810
                                                                        \
811
    case LOG_ALERT:                                                     \
812
        EventWritePSTCH_CBLTIN_ALERT_NL_AssumeEnabled(buff_as_wstr_data); \
813
        break;                                                          \
814
                                                                        \
815
    case LOG_CRIT:                                                      \
816
        EventWritePSTCH_CBLTIN_CRIT_NL_AssumeEnabled(buff_as_wstr_data); \
817
        break;                                                          \
818
                                                                        \
819
    case LOG_ERR:                                                       \
820
        EventWritePSTCH_CBLTIN_ERR_NL_AssumeEnabled(buff_as_wstr_data); \
821
        break;                                                          \
822
                                                                        \
823
    case LOG_WARNING:                                                   \
824
        EventWritePSTCH_CBLTIN_WARNING_NL_AssumeEnabled(buff_as_wstr_data); \
825
        break;                                                          \
826
                                                                        \
827
    case LOG_NOTICE:                                                    \
828
        EventWritePSTCH_CBLTIN_NOTICE_NL_AssumeEnabled(buff_as_wstr_data); \
829
        break;                                                          \
830
                                                                        \
831
    case LOG_INFO:                                                      \
832
        EventWritePSTCH_CBLTIN_INFO_NL_AssumeEnabled(buff_as_wstr_data); \
833
        break;                                                          \
834
                                                                        \
835
    case LOG_DEBUG:                                                     \
836
        EventWritePSTCH_DEBUG_NL(buff_as_wstr_data);                    \
837
        break;                                                          \
838
                                                                        \
839
    default:                                                            \
840
    {                                                                   \
841
        std::wstring _priority_as_wstr(std::to_wstring(_priority));     \
842
        EventWritePSTCH_CBLTIN_EMERG_NL_AssumeEnabled(                  \
843
                                             _priority_as_wstr.data()); \
844
                                                                        \
845
        EventWritePSTCH_CBLTIN_EMERG_NL_AssumeEnabled(buff_as_wstr_data); \
846
        break;                                                          \
847
    }                                                                   \
848
    }                                                                   \
849
}
850
851
#define WIN_LOG_BY_PRIORITY                                             \
852
    WIN_LOG_BY_PRIORITY_CSTR(&(buff[0]))
853
854
#endif
855
856
// ---------------------------------------------------------------------------
857
858
static const char * levelCStr(int _pri)
859
0
{
860
0
    const char * res = nullptr;
861
0
    switch(_pri)
862
0
    {
863
0
    case LOG_ALERT:
864
0
        res = "ALERT";
865
0
        break;
866
867
0
    case LOG_WARNING:
868
0
        res = "WRN";
869
0
        break;
870
871
0
    case LOG_INFO:
872
0
        res = "INF";
873
0
        break;
874
875
0
    case LOG_DEBUG:
876
0
        res = "DBG";
877
0
        break;
878
879
0
    case LOG_ERR:
880
0
        res = "ERR";
881
0
        break;
882
883
0
    default:
884
0
        res = "UNKNOWN";
885
0
        break;
886
0
    }
887
888
0
    return(res);
889
0
}
890
891
static int logToStdOutMaybeErr(int _priority, bool _andPrintf,
892
                               const char * _str)
893
0
{
894
0
    if (!_str)
895
0
        _str = "";
896
897
0
    char dAndT[256];
898
0
    dAndT[0] = 0;
899
0
    if ((_andPrintf)
900
0
        #ifndef DEBUG
901
0
        || (_priority >= LOG_WARNING)
902
0
        #endif
903
0
        )
904
0
    {
905
0
        PS_STRLCPY(&(dAndT[0]), "<No Timestamp>", sizeof(dAndT));
906
907
0
        time_t t = time(nullptr);
908
0
        if (t >= 0)
909
0
        {
910
0
            struct tm this_tm;
911
0
            memset(&this_tm, 0, sizeof(this_tm));
912
0
            struct tm * tm_ptr = PST_LOCALTIME_R(&t, &this_tm);
913
0
            if (tm_ptr)
914
0
            {
915
0
                snprintf(&(dAndT[0]), sizeof(dAndT)-9, "%d %02d:%02d:%02d",
916
0
                         tm_ptr->tm_mday,
917
0
                         tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);
918
0
            }
919
0
        }
920
0
    }
921
922
923
0
    int print_res = 0;
924
925
0
    if (_andPrintf)
926
0
        print_res = fprintf(stdout, "%s %s %s\n",
927
0
                            &(dAndT[0]), levelCStr(_priority), _str);
928
929
0
    #ifndef DEBUG
930
0
    if (_priority >= LOG_WARNING)
931
0
    {
932
0
        int stderr_print_res =
933
0
            fprintf(stderr, "%s %s: %s\n",
934
0
                    &(dAndT[0]), levelCStr(_priority), _str);
935
0
        if (!_andPrintf)
936
0
            print_res = stderr_print_res;
937
0
    }
938
0
    #endif
939
940
0
    return(print_res);
941
0
}
942
943
944
// ---------------------------------------------------------------------------
945
946
void PSLogging::log(int _priority, bool _andPrintf,
947
                    const char * _format, va_list _ap)
948
0
{
949
0
    char buff[2048];
950
0
    buff[0] = '(';
951
0
    if (snprintProcessAndThread(&(buff[1]),
952
0
                                sizeof(buff)-3-strlen(gLogEntryPrefix)) >= 0)
953
0
        PS_STRLCAT(&(buff[0]), " ", sizeof(buff)-8);
954
0
    PS_STRLCAT(&(buff[0]), gLogEntryPrefix, sizeof(buff)-8);
955
0
    PS_STRLCAT(&(buff[0]), ") ", sizeof(buff)-8);
956
957
0
    char * remaining_buff = &(buff[0]) + strlen(buff);
958
0
    size_t remaining_buff_size = sizeof(buff) - strlen(buff);
959
960
0
    #ifdef __clang__
961
    // Temporarily disable -Wformat-nonliteral
962
0
    #pragma clang diagnostic push
963
0
    #pragma clang diagnostic ignored "-Wformat-nonliteral"
964
0
    #endif
965
0
    int res = vsnprintf(remaining_buff, remaining_buff_size-2, _format, _ap);
966
0
    #ifdef __clang__
967
0
    #pragma clang diagnostic pop
968
0
    #endif
969
970
0
    if ((res < 0) && (_priority >= LOG_WARNING))
971
0
    {
972
0
        logToStdOutMaybeErr(LOG_ALERT, _andPrintf,
973
0
                            "vsnprintf failed for log in PSLogging::log\n");
974
975
0
        #ifdef __clang__
976
        // Temporarily disable -Wformat-nonliteral
977
0
        #pragma clang diagnostic push
978
0
        #pragma clang diagnostic ignored "-Wformat-nonliteral"
979
0
        #endif
980
        #ifdef PIST_USE_OS_LOG
981
          if (_priority < LOG_CRIT)
982
            _priority = LOG_CRIT;
983
984
          // Note: We do two separate os_log calls here, in case _format is
985
          // messed up in a way that both stops vsnprintf from working above,
986
          // and will stop os_log from working when using _format as a string
987
          // here
988
          OS_LOG_BY_PRIORITY_FORMAT_ARG("%s",
989
                                        "Unable to log, vsnprintf failed");
990
          if (_format)
991
              OS_LOG_BY_PRIORITY_FORMAT_ARG(
992
                  "Failing log vsnprintf format: %s", _format);
993
        #elif defined _IS_WINDOWS
994
          WIN_LOG_BY_PRIORITY_CSTR(
995
                                         "Unable to log, vsnprintf failed");
996
          if (_format)
997
              WIN_LOG_BY_PRIORITY_CSTR(_format);
998
999
        #else
1000
0
          vsyslog(_priority, _format, _ap);
1001
0
        #endif
1002
0
        #ifdef __clang__
1003
0
        #pragma clang diagnostic pop
1004
0
        #endif
1005
0
    }
1006
0
    else
1007
0
    {
1008
        #ifdef PIST_USE_OS_LOG
1009
        OS_LOG_BY_PRIORITY;
1010
        #elif defined _IS_WINDOWS
1011
        WIN_LOG_BY_PRIORITY;
1012
        #else
1013
0
        syslog(_priority, "%s", &(buff[0]));
1014
0
        #endif
1015
1016
0
        logToStdOutMaybeErr(_priority, _andPrintf, &(buff[0]));
1017
0
    }
1018
0
}
1019
1020
void PSLogging::log(int _priority, bool _andPrintf, const char * _str)
1021
0
{
1022
0
    if (!_str)
1023
0
        return;
1024
1025
0
    char buff[2048];
1026
0
    buff[0] = '(';
1027
0
    if (snprintProcessAndThread(&(buff[1]),
1028
0
                                sizeof(buff)-3-strlen(gLogEntryPrefix)) >= 0)
1029
0
    PS_STRLCAT(&(buff[0]), " ", sizeof(buff)-8);
1030
0
    PS_STRLCAT(&(buff[0]), gLogEntryPrefix, sizeof(buff)-8);
1031
0
    PS_STRLCAT(&(buff[0]), ") ", sizeof(buff)-8);
1032
1033
0
    char * remaining_buff = &(buff[0]) + strlen(buff);
1034
0
    size_t remaining_buff_size = sizeof(buff) - strlen(buff);
1035
1036
0
    int res = snprintf(remaining_buff, remaining_buff_size-2, "%s", _str);
1037
0
    if ((res < 0) && (_priority >= LOG_WARNING))
1038
0
    {
1039
0
        logToStdOutMaybeErr(LOG_ALERT, _andPrintf,
1040
0
                            "snprintf failed for log in PSLogging::log");
1041
1042
        #ifdef PIST_USE_OS_LOG
1043
          OS_LOG_BY_PRIORITY_FORMAT_ARG("%s", _str);
1044
        #elif defined _IS_WINDOWS
1045
          WIN_LOG_BY_PRIORITY_CSTR(_str);
1046
        #else
1047
0
          syslog(_priority, "%s", _str);
1048
0
        #endif
1049
1050
0
        logToStdOutMaybeErr(_priority, _andPrintf, _str);
1051
0
    }
1052
0
    else
1053
0
    {
1054
        #ifdef PIST_USE_OS_LOG
1055
        OS_LOG_BY_PRIORITY;
1056
        #elif defined _IS_WINDOWS
1057
        WIN_LOG_BY_PRIORITY;
1058
        #else
1059
0
        syslog(_priority, "%s", &(buff[0]));
1060
0
        #endif
1061
1062
0
        logToStdOutMaybeErr(_priority, _andPrintf, &(buff[0]));
1063
0
    }
1064
0
}
1065
1066
/*****************************************************************************/
1067
1068
static void PSLogPrv(int _priority, bool _andPrintf,
1069
                     const char * _format, va_list _ap)
1070
0
{
1071
0
    #ifndef DEBUG
1072
0
    if (_priority == LOG_DEBUG)
1073
0
        return;
1074
0
    #endif
1075
0
    if (!_format)
1076
0
        return;
1077
1078
0
    PSLogging::getPSLogging()->log(_priority, _andPrintf, _format, _ap);
1079
0
}
1080
1081
static void PSLogStrPrv(int _priority, bool _andPrintf, const char * _str)
1082
0
{
1083
0
    #ifndef DEBUG
1084
0
    if (_priority == LOG_DEBUG)
1085
0
        return;
1086
0
    #endif
1087
0
    if (!_str)
1088
0
        return;
1089
1090
0
    PSLogging::getPSLogging()->log(_priority, _andPrintf, _str);
1091
0
}
1092
1093
// ---------------------------------------------------------------------------
1094
1095
extern "C" void PSLogNoLocFn(int _pri, bool _andPrintf,
1096
                             const char * _format, ...)
1097
0
{
1098
0
    int tmp_errno = errno; // See note on preserving errno in PSLogFn
1099
1100
0
    if (!_format)
1101
0
        return;
1102
1103
    #ifdef _IS_WINDOWS
1104
    // getLogToStdoutAsWell reads the
1105
    // "HKCU:\Software\pistacheio\pistache\psLogToStdoutAsWell" property from
1106
    // the Windows registry.
1107
    const DWORD log_to_stdout_as_well = getLogToStdoutAsWell();
1108
    if (log_to_stdout_as_well == 10)
1109
        _andPrintf = false;
1110
    else
1111
        _andPrintf |= (log_to_stdout_as_well != 0);
1112
    #endif
1113
1114
0
    va_list ap;
1115
0
    va_start(ap, _format);
1116
0
    PSLogPrv(_pri, _andPrintf, _format, ap);
1117
0
    va_end(ap);
1118
1119
0
    errno = tmp_errno;
1120
0
}
1121
1122
extern "C" void PSLogFn(int _pri, bool _andPrintf,
1123
                        const char * f, int l, const char * m,
1124
                        const char * _format, ...)
1125
0
{
1126
0
    if (!_format)
1127
0
        return;
1128
1129
    #ifdef _IS_WINDOWS
1130
    // getLogToStdoutAsWell reads the
1131
    // "HKCU:\Software\pistacheio\pistache\psLogToStdoutAsWell" property from
1132
    // the Windows registry.
1133
    const DWORD log_to_stdout_as_well = getLogToStdoutAsWell();
1134
    if (log_to_stdout_as_well == 10)
1135
        _andPrintf = false;
1136
    else
1137
        _andPrintf |= (log_to_stdout_as_well != 0);
1138
    #endif
1139
1140
    // We preserve errno for this function since i) We don't want the act of
1141
    // logging (e.g. an error) to alter errno, even if the logging fails; and
1142
    // ii) Apple's os_log_xxx appears to set errno to zero even when successful
1143
    // (and the macOS man page for os_log has example code saying "os_log does
1144
    // not preserve errno").
1145
0
    int tmp_errno = errno;
1146
1147
0
    char bname_buff[PST_MAXPATHLEN+6];
1148
0
    if ((f) && (f[0]))
1149
0
    {
1150
0
        char * new_f = PS_BASENAME_R(f, &(bname_buff[0]));
1151
0
        if ((new_f) && (new_f[0]))
1152
0
            f = new_f;
1153
0
    }
1154
1155
0
    const unsigned int form_and_args_buf_size = 2048;
1156
0
    char form_and_args_buf[form_and_args_buf_size + 6];
1157
0
    form_and_args_buf[0] = 0;
1158
1159
0
    { // encapsulate
1160
0
        va_list ap;
1161
0
        va_start(ap, _format);
1162
0
        #ifdef __clang__
1163
        // Temporarily disable -Wformat-nonliteral
1164
0
        #pragma clang diagnostic push
1165
0
        #pragma clang diagnostic ignored "-Wformat-nonliteral"
1166
0
        #endif
1167
0
        int pos = vsnprintf(&(form_and_args_buf[0]),
1168
0
                            form_and_args_buf_size, _format, ap);
1169
0
        #ifdef __clang__
1170
0
        #pragma clang diagnostic pop
1171
0
        #endif
1172
1173
0
        va_end(ap);
1174
0
        if (pos >= (int) form_and_args_buf_size)
1175
0
            PS_STRLCAT(&(form_and_args_buf[0]), "...", form_and_args_buf_size);
1176
0
    }
1177
1178
0
    const unsigned int sizeof_buf = 4096;
1179
0
    unsigned int sizeof_buf_ex_form_and_args =
1180
0
                                       sizeof_buf - form_and_args_buf_size - 6;
1181
0
    char buf[sizeof_buf + 6];
1182
0
    char * buf_ptr = &(buf[0]);
1183
0
    buf_ptr[0] = 0;
1184
0
    int ln = 0;
1185
0
    if (f || m)
1186
0
    {
1187
0
        if (f && m)
1188
0
            ln= snprintf(buf_ptr, sizeof_buf_ex_form_and_args,
1189
0
                         "%s:%d in %s()",f,l,m);
1190
0
        else if (f)
1191
0
            ln = snprintf(buf_ptr, sizeof_buf_ex_form_and_args,
1192
0
                          "%s:%d", f, l);
1193
0
        else
1194
0
            ln= snprintf(buf_ptr,sizeof_buf_ex_form_and_args,
1195
0
                         "line %d in %s()",l, m);
1196
1197
0
        if (ln >= (int) sizeof_buf_ex_form_and_args)
1198
0
            PS_STRLCAT(buf_ptr, "...", sizeof_buf);
1199
0
    }
1200
1201
0
    if (form_and_args_buf[0])
1202
0
    { // Not empty string
1203
0
        PS_STRLCAT(buf_ptr, ": ", sizeof_buf);
1204
0
        PS_STRLCAT(buf_ptr, &(form_and_args_buf[0]), sizeof_buf);
1205
0
    }
1206
1207
0
    PSLogStrPrv(_pri, _andPrintf, buf);
1208
1209
0
    errno = tmp_errno;
1210
0
}
1211
1212
// ---------------------------------------------------------------------------
1213
1214
// If using SysLog (i.e. on Linux), if setPsLogCategory is called with NULL or
1215
// zero-length string then pistachio does not call openlog; and if
1216
// setPsLogCategory is called with a non-empty string before pistachio logs
1217
// anything then the _category string will be passed to openlog as the "ident"
1218
// parm upon the first pistachio log; or if setPsLogCategory is not called,
1219
// then pistachio will assign a 5-letter ident based on the executable name.
1220
//
1221
// Note that if (and this is NOT RECOMMENDED - instead get the app to call
1222
// openlog itself before anything is logged) setPsLogCategory is called with
1223
// NULL or empty string, but then pistachio logs something before the
1224
// application can call openlog on its own account, then syslog will
1225
// effectively call openlog itself using the app executable name for the ident.
1226
//
1227
// If using Apple "unified logging" (aka "os_log"), if setPsLogCategory is
1228
// called with a non-null and non-zero-length value, then that value is used as
1229
// the os_log category. Otherwise, the first time something is logged,
1230
// pistachio assigns its own 5-letter category name derived from the executable
1231
// name
1232
//
1233
// In either case, calling setPsLogCategory is optional
1234
extern "C" void setPsLogCategory(const char * _category)
1235
0
{
1236
0
    if ((!_category) || (!strlen(_category)))
1237
0
    {
1238
0
        gSetPsLogCategoryCalledWithNull = true;
1239
0
        return;
1240
0
    }
1241
1242
0
    if (strlen(_category) >= PST_MAXPATHLEN)
1243
0
        return;
1244
1245
0
    gSetPsLogCategoryCalledWithNull = false;
1246
0
    PS_STRLCPY(&(gIdentBuff[0]), _category, PST_MAXPATHLEN);
1247
0
}
1248
1249
// ---------------------------------------------------------------------------
1250
1251
// PSLogOss psLogOss;
1252
1253
// Implementation strategy:
1254
// https://stackoverflow.com/questions/13703823/a-custom-ostream
1255
// Create a streambuf with it's own sync function, initiatize ostream with the
1256
// streambuf