-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathsession.cpp
More file actions
139 lines (130 loc) · 4.93 KB
/
Copy pathsession.cpp
File metadata and controls
139 lines (130 loc) · 4.93 KB
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
/*
Shared timestamp-naming + mkdir-p helpers for TlogWriter / BinlogWriter.
*/
#include "session.h"
#include <dirent.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
int mkpath_0700(const char *path)
{
char tmp[1024];
snprintf(tmp, sizeof(tmp), "%s", path);
size_t n = strlen(tmp);
for (size_t i = 1; i <= n; i++) {
if (tmp[i] == '/' || tmp[i] == 0) {
char saved = tmp[i];
tmp[i] = 0;
if (mkdir(tmp, 0700) != 0 && errno != EEXIST) {
return -1;
}
tmp[i] = saved;
}
}
return 0;
}
void session_time_strings(time_t utc, bool use_offset, double tz_offset_hours,
char *datedir, size_t datedir_len,
char *name, size_t name_len)
{
struct tm tm {};
struct tm *r;
if (use_offset && isfinite(tz_offset_hours)) {
// Explicit fixed GMT offset: apply it then format with gmtime so
// the machine's own timezone plays no part. No DST — a fixed
// offset by design. Clamp to the valid range before the
// arithmetic so a malformed stored value (e.g. use_tz set on a
// hand-edited record) can't overflow time_t in llround/add.
double off = tz_offset_hours;
if (off < -12.0) off = -12.0;
if (off > 14.0) off = 14.0;
time_t shifted = utc + (time_t)llround(off * 3600.0);
r = gmtime_r(&shifted, &tm);
} else {
// Default (KEY_FLAG_USE_TZ clear, or a non-finite offset): name
// in the server's local timezone, as configured on the host —
// the least-surprising default and what the pre-timestamp
// naming did.
r = localtime_r(&utc, &tm);
}
if (r == nullptr) {
// gmtime_r/localtime_r only fail on absurd inputs; format a
// zeroed tm rather than uninitialised stack.
memset(&tm, 0, sizeof(tm));
}
snprintf(datedir, datedir_len, "%04d-%02d-%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
snprintf(name, name_len, "%04d_%02d_%02d_%02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
// Every extension a session can produce. A basename is only free if
// none of them is taken: the files of one session share a name, so
// handing back a name that any of them already occupies would append
// into (or truncate) another session's log.
static const char *SESSION_EXTS[] = {
".tlog", ".bin", ".v1.ts", ".v2.ts", ".v3.ts",
};
// True if no session file of any kind exists under this basename.
static bool basename_free(const char *dir, const char *candidate)
{
for (const char *ext : SESSION_EXTS) {
char p[2048];
snprintf(p, sizeof(p), "%s/%s%s", dir, candidate, ext);
struct stat st;
if (stat(p, &st) == 0) {
return false;
}
}
return true;
}
void session_unique_basename(const char *base_dir, uint32_t port2,
const char *datedir,
char *name, size_t name_len)
{
char base[64];
snprintf(base, sizeof(base), "%s", name);
char dir[1024];
snprintf(dir, sizeof(dir), "%s/%u/%s", base_dir, unsigned(port2), datedir);
// Plain basename, then "-2", "-3", … — every candidate is checked
// for occupancy across BOTH extensions, so we never hand back a name
// that would truncate (.bin) or append into (.tlog) another session's
// log. A single "-N" suffix keeps the name browsable by the web UI.
// The cap is a safety bound; the first free slot is normally "-2".
for (int suffix = 1; suffix < 100000; suffix++) {
char candidate[96];
if (suffix == 1) {
snprintf(candidate, sizeof(candidate), "%s", base);
} else {
snprintf(candidate, sizeof(candidate), "%s-%d", base, suffix);
}
if (basename_free(dir, candidate)) {
snprintf(name, name_len, "%s", candidate);
return;
}
}
// 100000 files in one second/dir cannot happen; as an absolute last
// resort append the pid+nanoseconds as ONE numeric suffix (still a
// single "-N" the web UI accepts) and still check occupancy, so we
// never return an occupied name.
for (int i = 0; i < 100; i++) {
struct timespec ts {};
clock_gettime(CLOCK_REALTIME, &ts);
char candidate[96];
snprintf(candidate, sizeof(candidate), "%s-%d%09ld",
base, int(getpid()), long(ts.tv_nsec));
if (basename_free(dir, candidate)) {
snprintf(name, name_len, "%s", candidate);
return;
}
}
// Unreachable: there is genuinely no free name. Leave `name` as the
// plain base (its value on entry) — no worse than the impossible
// exhaustion above.
snprintf(name, name_len, "%s", base);
}