File: log.c

package info (click to toggle)
charliecloud 0.43-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,116 kB
  • sloc: python: 6,021; sh: 4,284; ansic: 3,863; makefile: 598
file content (281 lines) | stat: -rw-r--r-- 9,025 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/* Copyright © Charliecloud contributors. */

#define _GNU_SOURCE
#include "config.h"

#include <string.h>
#include <unistd.h>

#include "all.h"

/** Constants **/

/* Text colors. Note leading escape characters (U+001B), which don’t always
   show up depending on your viewer.

   In principle, we should be using a library for this, e.g. terminfo(5).
   However, moderately thorough web searching suggests that pretty much any
   modern terminal will support 256-color ANSI codes, and this is way
   simpler [1]. Probably should coordinate these colors with the Python code
   somehow.

   [1]: https://stackoverflow.com/a/3219471 */
static const char COLOUR_CYAN_DARK[] =  "";
static const char COLOUR_CYAN_LIGHT[] = "";
//static const char COLOUR_GRAY[] =       "";
static const char COLOUR_RED[] =        "";
static const char COLOUR_RED_BOLD[] =   "";
static const char COLOUR_RESET[] =      "";
static const char COLOUR_YELLOW[] =     "";
static const char *_LL_COLOURS[] = { COLOUR_RED_BOLD,     // fatal
                                     COLOUR_RED_BOLD,     // stderr
                                     COLOUR_RED,          // warning
                                     COLOUR_YELLOW,       // info
                                     COLOUR_CYAN_LIGHT,   // verbose
                                     COLOUR_CYAN_DARK,    // debug
                                     COLOUR_CYAN_DARK };  // trace
/* This lets us index by verbosity, which can be negative. */
static const char **LL_COLOURS = _LL_COLOURS + 3;

/* Number of supplemental GIDs we can deal with. */
static const size_t SUPP_GIDS_MAX = 128;


/** External variables **/

/* If true, exit abnormally on fatal error. Set in ch-run.c during argument
   parsing, so will always be default value before that. */
bool abort_fatal = false;

/* If true, use coloured logging. Set in ch-run.c. */
bool log_color_p = false;

/* Level of chatter on stderr. */
enum log_level verbose;

/* List of warnings to be re-printed on exit. This is a buffer of shared memory
   allocated by mmap(2), structured as a sequence of null-terminated character
   strings. Warnings that do not fit in this buffer will be lost, though we
   allocate enough memory that this is unlikely. See “string_append()” for
   more details. */
char *warnings;

/* Current byte offset from start of “warnings” buffer. This gives the address
   where the next appended string will start. This means that the null
   terminator of the previous string is warnings_offset - 1. */
size_t warnings_offset = 0;


/** Function prototypes (private) **/

void msgv(enum log_level level, const char *file, int line, int errno_,
          const char *fmt, va_list ap)
         __attribute__ ((format (printf, 5, 0)));


/** Functions **/

/* If verbose enough, print uids and gids on stderr prefixed with where.

   FIXME: Should change to DEBUG(), but that will give the file/line within
   this function, which we don’t want. */
void log_ids(const char *func, int line)
{
   uid_t ruid, euid, suid;
   gid_t rgid, egid, sgid;
   gid_t supp_gids[SUPP_GIDS_MAX];
   int supp_gid_ct;

   if (verbose >= LL_TRACE + 1) {  // don’t bother b/c haven’t needed in ages
      Z_e (getresuid(&ruid, &euid, &suid));
      Z_e (getresgid(&rgid, &egid, &sgid));
      if (log_color_p)
         T_e (EOF != fputs(LL_COLOURS[LL_TRACE], stderr));
      fprintf(stderr, "%s %d: uids=%d,%d,%d, gids=%d,%d,%d + ", func, line,
              ruid, euid, suid, rgid, egid, sgid);
      supp_gid_ct = getgroups(SUPP_GIDS_MAX, supp_gids);
      if (supp_gid_ct == -1) {
         T_e (errno == EINVAL);
         Tf_ (0, "more than %zu groups", SUPP_GIDS_MAX);
      }
      for (int i = 0; i < supp_gid_ct; i++) {
         if (i > 0)
            fprintf(stderr, ",");
         fprintf(stderr, "%d", supp_gids[i]);
      }
      fprintf(stderr, "\n");
      if (log_color_p)
         T_e (EOF != fputs(COLOUR_RESET, stderr));
      Z_e (fflush(stderr));
   }
}

/* Set up logging. Note ch-run(1) specifies a bunch of color synonyms; this
   translation happens during argument parsing.*/
void logging_init(enum log_color_when when, enum log_test test)
{
   // set up colors
   switch (when) {
   case LL_COLOR_AUTO:
      if (isatty(fileno(stderr)))
         log_color_p = true;
      else {
         T_e (errno == ENOTTY);
         log_color_p = false;
      }
      break;
   case LL_COLOR_YES:
      log_color_p = true;
      break;
   case LL_COLOR_NO:
      log_color_p = false;
      break;
   case LL_COLOR_NULL:
      T__ (0);  // unreachable
      break;
   }

   // test logging
   if (test >= LL_TEST_YES) {
      TRACE("trace");
      DEBUG("debug");
      VERBOSE("verbose");
      INFO("info");
      WARNING("warning");
      if (test >= LL_TEST_FATAL)
         FATAL(-1, "the program failed inexplicably (\"log-fail\" specified)");
      exit(0);
   }
}

/* Print a formatted message on stderr if the level warrants it. */
void msg(enum log_level level, const char *file, int line, int errno_,
         const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   msgv(level, file, line, errno_, fmt, ap);
   va_end(ap);
}

void msg_error(const char *file, int line, int errno_,
               const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   /* We print errors at LL_FATAL because, according to our documentation,
      errors are never suppressed. Perhaps we need to rename this log level
      (see issue #1914). */
   msgv(LL_FATAL, file, line, errno_, fmt, ap);
   va_end(ap);
}

/* Note that msg_fatal doesn’t call msg_error like we do in the Python code
   because the variable number of arguments make it easier to simply define
   separate functions. */
noreturn msg_fatal(const char *file, int line, int errno_,
                        const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   msgv(LL_FATAL, file, line, errno_, fmt, ap);
   va_end(ap);

   if (abort_fatal)
      abort();
   else
      exit(EXIT_ERR_MISC);
}

/* va_list form of msg(). */
void msgv(enum log_level level, const char *file, int line, int errno_,
          const char *fmt, va_list ap)
{
   // note: all components contain appropriate leading/trailing space
   char *text_formatted;  // caller’s message, formatted
   char *level_prefix;    // level prefix
   char *errno_code;      // errno code/number
   char *errno_desc;      // errno description
   char *text_full;       // complete text but w/o color codes
   const char * colour;          // ANSI codes for color
   const char * colour_reset;    // ANSI codes to reset color

   if (level > verbose)   // not verbose enough; do nothing
      return;

   // Format caller message.
   if (fmt == NULL)
      text_formatted = "assertion failure: please report this bug";
   else
      text_formatted = vasprintf_ch(fmt, ap);

   // Prefix some of the levels.
   switch (level) {
   case LL_FATAL:
      level_prefix = "error: ";   // "fatal" too morbid for users
      break;
   case LL_WARNING:
      level_prefix = "warning: ";
      break;
   default:
      level_prefix = "";
      break;
   }

   // errno.
   if (errno_ < 0) {
      errno_code = "";
      errno_desc = "";
   } else {
      errno_code = cat(" ", errno_nerd_str(errno_));
      errno_desc = asprintf_ch(": %s", strerror(errno_));
   }

   // Color.
   if (log_color_p) {
      colour = LL_COLOURS[level];
      colour_reset = COLOUR_RESET;
   } else {
      colour = "";
      colour_reset = "";
   };

   // Format and print.
   text_full = asprintf_ch("%s[%d]: %s%s%s (%s:%d%s)",
                           program_invocation_short_name, getpid(),
                           level_prefix, text_formatted, errno_desc,
                           file, line, errno_code);
   fprintf(stderr, "%s%s%s\n", colour, text_full, colour_reset);
   if (fflush(stderr))
      abort();  // can’t print an error b/c already trying to do that
   if (level == LL_WARNING)
      warnings_offset += string_append(warnings, text_full,
                                       WARNINGS_SIZE, warnings_offset);
}

/* Reprint messages stored in “warnings” memory buffer. */
void warnings_reprint(void)
{
   size_t offset = 0;
   int warn_ct = buf_strings_count(warnings, WARNINGS_SIZE);

   if (warn_ct > 0) {
      if (log_color_p)
         T_e (EOF != fputs(LL_COLOURS[LL_WARNING], stderr));
      T_e (1 <= fprintf(stderr, "%s[%d]: reprinting first %d warning(s)\n",
                       program_invocation_short_name, getpid(), warn_ct));
      while (   warnings[offset] != 0
             || (offset < (WARNINGS_SIZE - 1) && warnings[offset+1] != 0)) {
         T_e (EOF != fputs(warnings + offset, stderr));
         T_e (EOF != fputc('\n', stderr));
         offset += strlen(warnings + offset) + 1;
      }
      if (log_color_p)
         T_e (EOF != fputs(COLOUR_RESET, stderr));
      if (fflush(stderr))
         abort();  // can't print an error b/c already trying to do that
   }
}