agoo 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +61 -0
- data/ext/agoo/agoo.c +19 -0
- data/ext/agoo/con.c +515 -0
- data/ext/agoo/con.h +46 -0
- data/ext/agoo/dtime.c +52 -0
- data/ext/agoo/dtime.h +10 -0
- data/ext/agoo/err.c +78 -0
- data/ext/agoo/err.h +48 -0
- data/ext/agoo/error_stream.c +92 -0
- data/ext/agoo/error_stream.h +13 -0
- data/ext/agoo/extconf.rb +13 -0
- data/ext/agoo/hook.c +74 -0
- data/ext/agoo/hook.h +33 -0
- data/ext/agoo/http.c +617 -0
- data/ext/agoo/http.h +13 -0
- data/ext/agoo/log.c +497 -0
- data/ext/agoo/log.h +106 -0
- data/ext/agoo/log_queue.h +30 -0
- data/ext/agoo/page.c +342 -0
- data/ext/agoo/page.h +39 -0
- data/ext/agoo/queue.c +191 -0
- data/ext/agoo/queue.h +39 -0
- data/ext/agoo/request.c +563 -0
- data/ext/agoo/request.h +36 -0
- data/ext/agoo/res.c +38 -0
- data/ext/agoo/res.h +28 -0
- data/ext/agoo/response.c +271 -0
- data/ext/agoo/response.h +33 -0
- data/ext/agoo/server.c +891 -0
- data/ext/agoo/server.h +47 -0
- data/ext/agoo/text.c +66 -0
- data/ext/agoo/text.h +24 -0
- data/ext/agoo/types.h +18 -0
- data/lib/agoo.rb +9 -0
- data/lib/agoo/version.rb +5 -0
- data/test/base_handler_test.rb +170 -0
- data/test/log_test.rb +269 -0
- data/test/rack_handler_test.rb +147 -0
- data/test/static_test.rb +81 -0
- data/test/tests.rb +8 -0
- metadata +159 -0
data/ext/agoo/http.h
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef __AGOO_HTTP_H__
|
4
|
+
#define __AGOO_HTTP_H__
|
5
|
+
|
6
|
+
#include <stdbool.h>
|
7
|
+
|
8
|
+
extern void http_init();
|
9
|
+
extern void http_header_ok(const char *key, int klen, const char *value, int vlen);
|
10
|
+
|
11
|
+
extern const char* http_code_message(int code);
|
12
|
+
|
13
|
+
#endif // __AGOO_HTTP_H__
|
data/ext/agoo/log.c
ADDED
@@ -0,0 +1,497 @@
|
|
1
|
+
// Copyright 2018 by Peter Ohler, All Rights Reserved
|
2
|
+
|
3
|
+
#include <dirent.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <string.h>
|
7
|
+
#include <sys/stat.h>
|
8
|
+
#include <sys/types.h>
|
9
|
+
#include <time.h>
|
10
|
+
|
11
|
+
#include "dtime.h"
|
12
|
+
#include "log.h"
|
13
|
+
|
14
|
+
// lower gives faster response but burns more CPU. This is a reasonable compromise.
|
15
|
+
#define RETRY_SECS 0.0001
|
16
|
+
#define NOT_WAITING 0
|
17
|
+
#define WAITING 1
|
18
|
+
#define NOTIFIED 2
|
19
|
+
#define RESET_COLOR "\033[0m"
|
20
|
+
#define RESET_SIZE 4
|
21
|
+
|
22
|
+
static const char log_name[] = "agoo.log";
|
23
|
+
static const char log_prefix[] = "agoo.log.";
|
24
|
+
static const char log_format[] = "%s/agoo.log.%d";
|
25
|
+
|
26
|
+
static struct _Color colors[] = {
|
27
|
+
{ .name = "black", .ansi = "\033[30;1m" },
|
28
|
+
{ .name = "red", .ansi = "\033[31;1m" },
|
29
|
+
{ .name = "green", .ansi = "\033[32;1m" },
|
30
|
+
{ .name = "yellow", .ansi = "\033[33;1m" },
|
31
|
+
{ .name = "blue", .ansi = "\033[34;1m" },
|
32
|
+
{ .name = "magenta", .ansi = "\033[35;1m" },
|
33
|
+
{ .name = "cyan", .ansi = "\033[36;1m" },
|
34
|
+
{ .name = "white", .ansi = "\033[37;1m" },
|
35
|
+
{ .name = "gray", .ansi = "\033[37m" },
|
36
|
+
{ .name = "dark_red", .ansi = "\033[31m" },
|
37
|
+
{ .name = "dark_green", .ansi = "\033[32m" },
|
38
|
+
{ .name = "brown", .ansi = "\033[33m" },
|
39
|
+
{ .name = "dark_blue", .ansi = "\033[34m" },
|
40
|
+
{ .name = "purple", .ansi = "\033[35m" },
|
41
|
+
{ .name = "dark_cyan", .ansi = "\033[36m" },
|
42
|
+
{ .name = NULL, .ansi = NULL }
|
43
|
+
};
|
44
|
+
|
45
|
+
static const char level_chars[] = { 'F', 'E', 'W', 'I', 'D', '?' };
|
46
|
+
|
47
|
+
static Color
|
48
|
+
find_color(const char *name) {
|
49
|
+
if (NULL != name) {
|
50
|
+
for (Color c = colors; NULL != c->name; c++) {
|
51
|
+
if (0 == strcasecmp(c->name, name)) {
|
52
|
+
return c;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
return NULL;
|
57
|
+
}
|
58
|
+
|
59
|
+
static bool
|
60
|
+
log_queue_empty(Log log) {
|
61
|
+
LogEntry head = atomic_load(&log->head);
|
62
|
+
LogEntry next = head + 1;
|
63
|
+
|
64
|
+
if (log->end <= next) {
|
65
|
+
next = log->q;
|
66
|
+
}
|
67
|
+
if (!head->ready && log->tail == next) {
|
68
|
+
return true;
|
69
|
+
}
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
73
|
+
static LogEntry
|
74
|
+
log_queue_pop(Log log, double timeout) {
|
75
|
+
LogEntry e = log->head;
|
76
|
+
LogEntry next;
|
77
|
+
|
78
|
+
if (e->ready) {
|
79
|
+
return e;
|
80
|
+
}
|
81
|
+
next = log->head + 1;
|
82
|
+
if (log->end <= next) {
|
83
|
+
next = log->q;
|
84
|
+
}
|
85
|
+
// If the next is the tail then wait for something to be appended.
|
86
|
+
for (int cnt = (int)(timeout / RETRY_SECS); atomic_load(&log->tail) == next; cnt--) {
|
87
|
+
// TBD poll would be better
|
88
|
+
if (cnt <= 0) {
|
89
|
+
return NULL;
|
90
|
+
}
|
91
|
+
dsleep(RETRY_SECS);
|
92
|
+
}
|
93
|
+
atomic_store(&log->head, next);
|
94
|
+
|
95
|
+
return log->head;
|
96
|
+
}
|
97
|
+
|
98
|
+
|
99
|
+
static int
|
100
|
+
jwrite(LogEntry e, FILE *file) {
|
101
|
+
// TBD make e->what JSON friendly
|
102
|
+
return fprintf(file, "{\"when\":%lld.%09lld,\"where\":\"%s\",\"level\":%d,\"what\":\"%s\"}\n",
|
103
|
+
(long long)(e->when / 1000000000LL),
|
104
|
+
(long long)(e->when % 1000000000LL),
|
105
|
+
e->cat->label,
|
106
|
+
e->cat->level,
|
107
|
+
(NULL == e->whatp ? e->what : e->whatp));
|
108
|
+
}
|
109
|
+
|
110
|
+
//I 2015/05/23 11:22:33.123456789 label: The contents of the what field.
|
111
|
+
static int
|
112
|
+
classic_write(Log log, LogEntry e, FILE *file) {
|
113
|
+
time_t t = (time_t)(e->when / 1000000000LL);
|
114
|
+
int hour = 0;
|
115
|
+
int min = 0;
|
116
|
+
int sec = 0;
|
117
|
+
long long frac = (long long)e->when % 1000000000LL;
|
118
|
+
char levelc = level_chars[e->cat->level];
|
119
|
+
int cnt = 0;
|
120
|
+
|
121
|
+
t += log->zone;
|
122
|
+
if (log->day_start <= t && t < log->day_end) {
|
123
|
+
t -= log->day_start;
|
124
|
+
hour = t / 3600;
|
125
|
+
min = t % 3600 / 60;
|
126
|
+
sec = t % 60;
|
127
|
+
} else {
|
128
|
+
struct tm *tm = gmtime(&t);
|
129
|
+
|
130
|
+
hour = tm->tm_hour;
|
131
|
+
min = tm->tm_min;
|
132
|
+
sec = tm->tm_sec;
|
133
|
+
sprintf(log->day_buf, "%04d/%02d/%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
|
134
|
+
log->day_start = t - (hour * 3600 + min * 60 + sec);
|
135
|
+
log->day_end = log->day_start + 86400;
|
136
|
+
}
|
137
|
+
if (log->colorize) {
|
138
|
+
cnt = fprintf(file, "%s%c %s%02d:%02d:%02d.%09lld %s: %s%s\n",
|
139
|
+
e->cat->color->ansi, levelc, log->day_buf, hour, min, sec, frac,
|
140
|
+
e->cat->label,
|
141
|
+
(NULL == e->whatp ? e->what : e->whatp),
|
142
|
+
RESET_COLOR);
|
143
|
+
} else {
|
144
|
+
cnt += fprintf(file, "%c %s%02d:%02d:%02d.%09lld %s: %s\n",
|
145
|
+
levelc, log->day_buf, hour, min, sec, frac,
|
146
|
+
e->cat->label,
|
147
|
+
(NULL == e->whatp ? e->what : e->whatp));
|
148
|
+
}
|
149
|
+
return cnt;
|
150
|
+
}
|
151
|
+
|
152
|
+
// Remove all file with sequence numbers higher than max_files. max_files is
|
153
|
+
// max number of archived version. It does not include the primary.
|
154
|
+
static void
|
155
|
+
remove_old_logs(Log log) {
|
156
|
+
struct dirent *de;
|
157
|
+
long seq;
|
158
|
+
char *end;
|
159
|
+
char path[1024];
|
160
|
+
DIR *dir = opendir(log->dir);
|
161
|
+
|
162
|
+
while (NULL != (de = readdir(dir))) {
|
163
|
+
if ('.' == *de->d_name || '\0' == *de->d_name) {
|
164
|
+
continue;
|
165
|
+
}
|
166
|
+
if (0 != strncmp(log_prefix, de->d_name, sizeof(log_prefix) - 1)) {
|
167
|
+
continue;
|
168
|
+
}
|
169
|
+
// Don't remove the primary log file.
|
170
|
+
if (0 == strcmp(log_name, de->d_name)) {
|
171
|
+
continue;
|
172
|
+
}
|
173
|
+
seq = strtol(de->d_name + sizeof(log_prefix) - 1, &end, 10);
|
174
|
+
if (log->max_files < seq) {
|
175
|
+
snprintf(path, sizeof(path), "%s/%s", log->dir, de->d_name);
|
176
|
+
remove(path);
|
177
|
+
}
|
178
|
+
}
|
179
|
+
closedir(dir);
|
180
|
+
}
|
181
|
+
|
182
|
+
static int
|
183
|
+
rotate(Err err, Log log) {
|
184
|
+
char from[1024];
|
185
|
+
char to[1024];
|
186
|
+
|
187
|
+
if (NULL != log->file) {
|
188
|
+
fclose(log->file);
|
189
|
+
log->file = NULL;
|
190
|
+
}
|
191
|
+
for (int seq = log->max_files; 0 < seq; seq--) {
|
192
|
+
snprintf(to, sizeof(to), log_format, log->dir, seq + 1);
|
193
|
+
snprintf(from, sizeof(from), log_format, log->dir, seq);
|
194
|
+
rename(from, to);
|
195
|
+
}
|
196
|
+
snprintf(to, sizeof(to), log_format, log->dir, 1);
|
197
|
+
snprintf(from, sizeof(from), "%s/%s", log->dir, log_name);
|
198
|
+
rename(from, to);
|
199
|
+
|
200
|
+
log->file = fopen(from, "w");
|
201
|
+
log->size = 0;
|
202
|
+
|
203
|
+
remove_old_logs(log);
|
204
|
+
|
205
|
+
return ERR_OK;
|
206
|
+
}
|
207
|
+
|
208
|
+
static void*
|
209
|
+
loop(void *ctx) {
|
210
|
+
Log log = (Log)ctx;
|
211
|
+
LogEntry e;
|
212
|
+
|
213
|
+
while (!log->done || !log_queue_empty(log)) {
|
214
|
+
if (NULL != (e = log_queue_pop(log, 0.5))) {
|
215
|
+
if (log->console) {
|
216
|
+
if (log->classic) {
|
217
|
+
classic_write(log, e, stdout);
|
218
|
+
} else {
|
219
|
+
jwrite(e, stdout);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
if (NULL != log->file) {
|
223
|
+
if (log->classic) {
|
224
|
+
log->size += classic_write(log, e, log->file);
|
225
|
+
} else {
|
226
|
+
log->size += jwrite(e, log->file);
|
227
|
+
}
|
228
|
+
if (log->max_size <= log->size) {
|
229
|
+
rotate(NULL, log);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
if (NULL != e->whatp) {
|
233
|
+
free(e->whatp);
|
234
|
+
}
|
235
|
+
e->ready = false;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
return NULL;
|
239
|
+
}
|
240
|
+
|
241
|
+
bool
|
242
|
+
log_flush(Log log, double timeout) {
|
243
|
+
timeout += dtime();
|
244
|
+
|
245
|
+
while (!log->done && !log_queue_empty(log)) {
|
246
|
+
if (timeout < dtime()) {
|
247
|
+
return false;
|
248
|
+
}
|
249
|
+
dsleep(0.001);
|
250
|
+
}
|
251
|
+
if (NULL != log->file) {
|
252
|
+
fflush(log->file);
|
253
|
+
}
|
254
|
+
return true;
|
255
|
+
}
|
256
|
+
|
257
|
+
static int
|
258
|
+
configure(Err err, Log log, VALUE options) {
|
259
|
+
if (Qnil != options) {
|
260
|
+
VALUE v;
|
261
|
+
|
262
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_dir"))))) {
|
263
|
+
rb_check_type(v, T_STRING);
|
264
|
+
strncpy(log->dir, StringValuePtr(v), sizeof(log->dir));
|
265
|
+
log->dir[sizeof(log->dir) - 1] = '\0';
|
266
|
+
}
|
267
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_max_files"))))) {
|
268
|
+
int max = FIX2INT(v);
|
269
|
+
|
270
|
+
if (1 <= max || max < 100) {
|
271
|
+
log->max_files = max;
|
272
|
+
} else {
|
273
|
+
rb_raise(rb_eArgError, "log_max_files must be between 1 and 100.");
|
274
|
+
}
|
275
|
+
}
|
276
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_max_size"))))) {
|
277
|
+
int max = FIX2INT(v);
|
278
|
+
|
279
|
+
if (1 <= max) {
|
280
|
+
log->max_size = max;
|
281
|
+
} else {
|
282
|
+
rb_raise(rb_eArgError, "log_max_size must be 1 or more.");
|
283
|
+
}
|
284
|
+
}
|
285
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_console"))))) {
|
286
|
+
log->console = (Qtrue == v);
|
287
|
+
}
|
288
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_classic"))))) {
|
289
|
+
log->classic = (Qtrue == v);
|
290
|
+
}
|
291
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_colorize"))))) {
|
292
|
+
log->colorize = (Qtrue == v);
|
293
|
+
}
|
294
|
+
if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("log_states"))))) {
|
295
|
+
if (T_HASH == rb_type(v)) {
|
296
|
+
LogCat cat = log->cats;
|
297
|
+
VALUE cv;
|
298
|
+
|
299
|
+
for (; NULL != cat; cat = cat->next) {
|
300
|
+
if (Qnil != (cv = rb_hash_lookup(v, ID2SYM(rb_intern(cat->label))))) {
|
301
|
+
if (Qtrue == cv) {
|
302
|
+
cat->on = true;
|
303
|
+
} else if (Qfalse == cv) {
|
304
|
+
cat->on = false;
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
} else {
|
309
|
+
rb_raise(rb_eArgError, "log_states must be a Hash.");
|
310
|
+
}
|
311
|
+
}
|
312
|
+
}
|
313
|
+
return ERR_OK;
|
314
|
+
}
|
315
|
+
|
316
|
+
static int
|
317
|
+
open_log_file(Err err, Log log) {
|
318
|
+
char path[1024];
|
319
|
+
|
320
|
+
snprintf(path, sizeof(path), "%s/%s", log->dir, log_name);
|
321
|
+
|
322
|
+
log->file = fopen(path, "a");
|
323
|
+
if (NULL == log->file) {
|
324
|
+
return err_no(err, "failed to open '%s'.", path);
|
325
|
+
}
|
326
|
+
log->size = ftell(log->file);
|
327
|
+
if (log->max_size <= log->size) {
|
328
|
+
return rotate(err, log);
|
329
|
+
}
|
330
|
+
return ERR_OK;
|
331
|
+
}
|
332
|
+
|
333
|
+
int
|
334
|
+
log_init(Err err, Log log, VALUE cfg) {
|
335
|
+
time_t t = time(NULL);
|
336
|
+
struct tm *tm = localtime(&t);
|
337
|
+
int qsize = 1024;
|
338
|
+
|
339
|
+
//log->cats = NULL; done outside of here
|
340
|
+
*log->dir = '\0';
|
341
|
+
log->file = NULL;
|
342
|
+
log->max_files = 3;
|
343
|
+
log->max_size = 100000000; // 100M
|
344
|
+
log->size = 0;
|
345
|
+
log->done = false;
|
346
|
+
log->console = true;
|
347
|
+
log->classic = true;
|
348
|
+
log->colorize = true;
|
349
|
+
log->zone = (int64_t)(timegm(tm) - t);
|
350
|
+
log->day_start = 0;
|
351
|
+
log->day_end = 0;
|
352
|
+
*log->day_buf = '\0';
|
353
|
+
log->thread = 0;
|
354
|
+
|
355
|
+
if (ERR_OK != configure(err, log, cfg)) {
|
356
|
+
return err->code;
|
357
|
+
}
|
358
|
+
if ('\0' != *log->dir) {
|
359
|
+
if (0 != mkdir(log->dir, 0770) && EEXIST != errno) {
|
360
|
+
return err_no(err, "Failed to create '%s'.", log->dir);
|
361
|
+
}
|
362
|
+
if (ERR_OK != open_log_file(err, log)) {
|
363
|
+
return err->code;
|
364
|
+
}
|
365
|
+
}
|
366
|
+
log->q = (LogEntry)malloc(sizeof(struct _LogEntry) * qsize);
|
367
|
+
log->end = log->q + qsize;
|
368
|
+
|
369
|
+
memset(log->q, 0, sizeof(struct _LogEntry) * qsize);
|
370
|
+
log->head = log->q;
|
371
|
+
log->tail = log->q + 1;
|
372
|
+
atomic_flag_clear(&log->push_lock);
|
373
|
+
log->wait_state = NOT_WAITING;
|
374
|
+
// Create when/if needed.
|
375
|
+
log->rsock = 0;
|
376
|
+
log->wsock = 0;
|
377
|
+
|
378
|
+
pthread_create(&log->thread, NULL, loop, log);
|
379
|
+
|
380
|
+
return ERR_OK;
|
381
|
+
}
|
382
|
+
|
383
|
+
void
|
384
|
+
log_close(Log log) {
|
385
|
+
log->done = true;
|
386
|
+
// TBD wake up loop like push does
|
387
|
+
log_cat_on(log, NULL, false);
|
388
|
+
if (0 != log->thread) {
|
389
|
+
pthread_join(log->thread, NULL);
|
390
|
+
log->thread = 0;
|
391
|
+
}
|
392
|
+
if (NULL != log->file) {
|
393
|
+
fclose(log->file);
|
394
|
+
log->file = NULL;
|
395
|
+
}
|
396
|
+
free(log->q);
|
397
|
+
log->q = NULL;
|
398
|
+
log->end = NULL;
|
399
|
+
if (0 < log->wsock) {
|
400
|
+
close(log->wsock);
|
401
|
+
}
|
402
|
+
if (0 < log->rsock) {
|
403
|
+
close(log->rsock);
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
void
|
408
|
+
log_cat_reg(Log log, LogCat cat, const char *label, LogLevel level, const char *color, bool on) {
|
409
|
+
cat->log = log;
|
410
|
+
strncpy(cat->label, label, sizeof(cat->label));
|
411
|
+
cat->label[sizeof(cat->label) - 1] = '\0';
|
412
|
+
cat->level = level;
|
413
|
+
cat->color = find_color(color);
|
414
|
+
cat->on = on;
|
415
|
+
cat->next = log->cats;
|
416
|
+
log->cats = cat;
|
417
|
+
}
|
418
|
+
|
419
|
+
void
|
420
|
+
log_cat_on(Log log, const char *label, bool on) {
|
421
|
+
LogCat cat;
|
422
|
+
|
423
|
+
for (cat = log->cats; NULL != cat; cat = cat->next) {
|
424
|
+
if (NULL == label || 0 == strcasecmp(label, cat->label)) {
|
425
|
+
cat->on = on;
|
426
|
+
break;
|
427
|
+
}
|
428
|
+
}
|
429
|
+
}
|
430
|
+
|
431
|
+
LogCat
|
432
|
+
log_cat_find(Log log, const char *label) {
|
433
|
+
LogCat cat;
|
434
|
+
|
435
|
+
for (cat = log->cats; NULL != cat; cat = cat->next) {
|
436
|
+
if (0 == strcasecmp(label, cat->label)) {
|
437
|
+
return cat;
|
438
|
+
}
|
439
|
+
}
|
440
|
+
return NULL;
|
441
|
+
}
|
442
|
+
|
443
|
+
void
|
444
|
+
log_catv(LogCat cat, const char *fmt, va_list ap) {
|
445
|
+
if (cat->on && !cat->log->done) {
|
446
|
+
Log log = cat->log;
|
447
|
+
struct timespec ts;
|
448
|
+
LogEntry e;
|
449
|
+
LogEntry tail;
|
450
|
+
int cnt;
|
451
|
+
va_list ap2;
|
452
|
+
|
453
|
+
va_copy(ap2, ap);
|
454
|
+
|
455
|
+
while (atomic_flag_test_and_set(&log->push_lock)) {
|
456
|
+
dsleep(RETRY_SECS);
|
457
|
+
}
|
458
|
+
// Wait for head to move on.
|
459
|
+
while (atomic_load(&log->head) == log->tail) {
|
460
|
+
dsleep(RETRY_SECS);
|
461
|
+
}
|
462
|
+
// TBD fill in the entry at tail
|
463
|
+
clock_gettime(CLOCK_REALTIME, &ts);
|
464
|
+
e = log->tail;
|
465
|
+
e->cat = cat;
|
466
|
+
e->when = (int64_t)ts.tv_sec * 1000000000LL + (int64_t)ts.tv_nsec;
|
467
|
+
e->whatp = NULL;
|
468
|
+
if ((int)sizeof(e->what) <= (cnt = vsnprintf(e->what, sizeof(e->what), fmt, ap))) {
|
469
|
+
e->whatp = (char*)malloc(cnt + 1);
|
470
|
+
|
471
|
+
if (NULL != e->whatp) {
|
472
|
+
vsnprintf(e->whatp, cnt + 1, fmt, ap2);
|
473
|
+
}
|
474
|
+
}
|
475
|
+
tail = log->tail + 1;
|
476
|
+
if (log->end <= tail) {
|
477
|
+
tail = log->q;
|
478
|
+
}
|
479
|
+
atomic_store(&log->tail, tail);
|
480
|
+
atomic_flag_clear(&log->push_lock);
|
481
|
+
va_end(ap2);
|
482
|
+
|
483
|
+
if (0 != log->wsock && WAITING == atomic_load(&log->wait_state)) {
|
484
|
+
if (write(log->wsock, ".", 1)) {}
|
485
|
+
atomic_store(&log->wait_state, NOTIFIED);
|
486
|
+
}
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
void
|
491
|
+
log_cat(LogCat cat, const char *fmt, ...) {
|
492
|
+
va_list ap;
|
493
|
+
|
494
|
+
va_start(ap, fmt);
|
495
|
+
log_catv(cat, fmt, ap);
|
496
|
+
va_end(ap);
|
497
|
+
}
|