1/*
2 This file is part of Mtproto-proxy Library.
3
4 Mtproto-proxy Library is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 Mtproto-proxy Library 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 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2009-2012 Vkontakte Ltd
18 2009-2012 Nikolai Durov
19 2009-2012 Andrey Lopatin
20 2012 Anton Maydell
21
22 Copyright 2014 Telegram Messenger Inc
23 2014 Anton Maydell
24*/
25
26#include <assert.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <limits.h>
30#include <math.h>
31#include <stdarg.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <time.h>
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <sys/types.h>
39#include <sys/file.h>
40#include <unistd.h>
41
42#include "kprintf.h"
43#include "precise-time.h"
44
45int verbosity;
46const char *logname;
47
48void reopen_logs_ext (int slave_mode) {
49 int fd;
50 fflush (stdout);
51 fflush (stderr);
52 if ((fd = open ("/dev/null", O_RDWR, 0)) != -1) {
53 dup2 (fd, 0);
54 dup2 (fd, 1);
55 dup2 (fd, 2);
56 if (fd > 2) {
57 close (fd);
58 }
59 }
60 if (logname && (fd = open (logname, O_WRONLY|O_APPEND|O_CREAT, 0640)) != -1) {
61 dup2 (fd, 1);
62 dup2 (fd, 2);
63 if (fd > 2) {
64 close (fd);
65 }
66 }
67 if (!slave_mode) {
68 vkprintf (1, "logs reopened.\n");
69 }
70}
71
72void reopen_logs (void) {
73 reopen_logs_ext (0);
74}
75
76int hexdump (const void *start, const void *end) {
77 char s[256];
78 const char *ptr = start;
79 while (ptr < (char *) end) {
80 int len = (const char *) end - ptr, i;
81 if (len > 16) {
82 len = 16;
83 }
84 int p = 0;
85 p += sprintf (s + p, "%08x", (int) (ptr - (char *) start));
86 for (i = 0; i < 16; i++) {
87 s[p ++] = ' ';
88 if (i == 8) {
89 s[p ++] = ' ';
90 }
91 if (i < len) {
92 p += sprintf (s + p, "%02x", (unsigned char) ptr[i]);
93 } else {
94 p += sprintf (s + p, " ");
95 }
96 }
97 s[p ++] = '\n';
98 nck_write (2, s, p);
99 ptr += 16;
100 }
101 return end - start;
102}
103
104
105double reindex_speed = (32 << 20);
106
107void kdb_write (int fd, const void *buf, long long count, const char *filename) {
108 assert (count >= 0);
109
110 static double total_count;
111 static double last_time;
112 int write_fail_count = 0;
113 while (count) {
114 long long l = !reindex_speed ? count : count >= (1 << 20) ? (1 << 20) : count;
115
116 if (reindex_speed) {
117 double t = get_utime_monotonic ();
118 total_count = total_count * exp ((last_time - t) * 0.1);
119 last_time = t;
120
121 if (total_count > reindex_speed) {
122 double k = log (total_count / reindex_speed) * 10;
123 assert (k >= 0);
124 struct timespec ts;
125 ts.tv_nsec = ((int)((k - floor (k)) * 1e9)) % 1000000000;
126 ts.tv_sec = (int)k;
127 nanosleep (&ts, 0);
128 }
129 }
130 long long w = write (fd, buf, l);
131 if (w <= 0) {
132 assert (-1 <= w);
133 if (write_fail_count < 10000 && (w == 0 || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
134 write_fail_count++;
135 continue;
136 }
137
138 fprintf (stderr, "kdb_write: write %lld bytes to the file '%s' returns %lld. %m\n", l, filename, w);
139 exit (1);
140 }
141 assert (w <= l);
142 write_fail_count = 0;
143
144 if (reindex_speed) {
145 static long long data_after_fsync;
146 data_after_fsync += w;
147 if (data_after_fsync >= (1 << 20)) {
148 if (fsync (fd) < 0) {
149 fprintf (stderr, "kdb_write: fsyncing file '%s' failed. %m\n", filename);
150 exit (1);
151 }
152 data_after_fsync = 0;
153 }
154 double t = get_utime_monotonic ();
155 total_count = total_count * exp ((last_time - t) * 0.1);
156 last_time = t;
157 total_count += w * 0.1;
158 }
159 count -= w;
160 buf += w;
161 }
162}
163
164static inline void kwrite_print_int (char **s, const char *name, int name_len, int i) {
165 if (i < 0) {
166 i = INT_MAX;
167 }
168
169 *--*s = ' ';
170 *--*s = ']';
171
172 do {
173 *--*s = i % 10 + '0';
174 i /= 10;
175 } while (i > 0);
176
177 *--*s = ' ';
178
179 while (--name_len >= 0) {
180 *--*s = name[name_len];
181 }
182
183 *--*s = '[';
184}
185
186int kwrite (int fd, const void *buf, int count) {
187 int old_errno = errno;
188
189#define S_BUF_SIZE 100
190#define S_DATA_SIZE 256
191 char s[S_BUF_SIZE + S_DATA_SIZE], *s_begin = s + S_BUF_SIZE;
192
193 kwrite_print_int (&s_begin, "time", 4, time (NULL));
194 kwrite_print_int (&s_begin, "pid" , 3, getpid ());
195
196 assert (s_begin >= s);
197
198 int s_count = s + S_BUF_SIZE - s_begin;
199 if (count <= S_DATA_SIZE) {
200 int i;
201 for (i = 0; i < count; i++) {
202 s[i + S_BUF_SIZE] = ((char *)buf)[i];
203 }
204 s_count += count;
205 count = 0;
206 }
207
208 int result = s_count + count;
209 while (s_count > 0) {
210 errno = 0;
211 int res = (int)write (fd, s_begin, (size_t)s_count);
212 if (errno && errno != EINTR) {
213 errno = old_errno;
214 return res;
215 }
216 if (!res) {
217 break;
218 }
219 if (res >= 0) {
220 s_begin += res;
221 s_count -= res;
222 }
223 }
224
225 while (count > 0) {
226 errno = 0;
227 int res = (int)write (fd, buf, (size_t)count);
228 if (errno && errno != EINTR) {
229 errno = old_errno;
230 return res;
231 }
232 if (!res) {
233 break;
234 }
235 if (res >= 0) {
236 buf += res;
237 count -= res;
238 }
239 }
240
241 errno = old_errno;
242 return result;
243#undef S_BUF_SIZE
244#undef S_DATA_SIZE
245}
246
247void kprintf (const char *format, ...) {
248 const int old_errno = errno;
249 struct tm t;
250 struct timeval tv;
251 char mp_kprintf_buf[PIPE_BUF];
252
253 if (gettimeofday (&tv, NULL) || !localtime_r (&tv.tv_sec, &t)) {
254 memset (&t, 0, sizeof (t));
255 }
256
257 int n = snprintf (mp_kprintf_buf, sizeof (mp_kprintf_buf), "[%d][%4d-%02d-%02d %02d:%02d:%02d.%06d local] ", getpid (), t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, (int) tv.tv_usec);
258 if (n < sizeof (mp_kprintf_buf) - 1) {
259 errno = old_errno;
260 va_list ap;
261 va_start (ap, format);
262 n += vsnprintf (mp_kprintf_buf + n, sizeof (mp_kprintf_buf) - n, format, ap);
263 va_end (ap);
264 }
265 if (n >= sizeof (mp_kprintf_buf)) {
266 n = sizeof (mp_kprintf_buf) - 1;
267 if (mp_kprintf_buf[n-1] != '\n') {
268 mp_kprintf_buf[n++] = '\n';
269 }
270 }
271 while (write (2, mp_kprintf_buf, n) < 0 && errno == EINTR);
272 //while (flock (2, LOCK_UN) < 0 && errno == EINTR);
273 errno = old_errno;
274}
275
276void nck_write (int fd, const void *data, size_t len) {
277 if (write (fd, data, len)) {}
278}
279
280void nck_pwrite (int fd, const void *data, size_t len, off_t offset) {
281 if (pwrite (fd, data, len, offset)) {}
282}
283