unix/signal.c
Go to the documentation of this file.
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  * Permission is hereby granted, free of charge, to any person obtaining a copy
3  * of this software and associated documentation files (the "Software"), to
4  * deal in the Software without restriction, including without limitation the
5  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6  * sell copies of the Software, and to permit persons to whom the Software is
7  * furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18  * IN THE SOFTWARE.
19  */
20 
21 #include "uv.h"
22 #include "internal.h"
23 
24 #include <assert.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #ifndef SA_RESTART
32 # define SA_RESTART 0
33 #endif
34 
35 typedef struct {
37  int signum;
39 
40 RB_HEAD(uv__signal_tree_s, uv_signal_s);
41 
42 
43 static int uv__signal_unlock(void);
46  int signum,
47  int oneshot);
48 static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events);
49 static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2);
50 static void uv__signal_stop(uv_signal_t* handle);
51 static void uv__signal_unregister_handler(int signum);
52 
53 
55 static struct uv__signal_tree_s uv__signal_tree =
57 static int uv__signal_lock_pipefd[2] = { -1, -1 };
58 
59 RB_GENERATE_STATIC(uv__signal_tree_s,
60  uv_signal_s, tree_entry,
62 
63 static void uv__signal_global_reinit(void);
64 
65 static void uv__signal_global_init(void) {
66  if (uv__signal_lock_pipefd[0] == -1)
67  /* pthread_atfork can register before and after handlers, one
68  * for each child. This only registers one for the child. That
69  * state is both persistent and cumulative, so if we keep doing
70  * it the handler functions will be called multiple times. Thus
71  * we only want to do it once.
72  */
73  if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit))
74  abort();
75 
77 }
78 
79 
80 UV_DESTRUCTOR(static void uv__signal_global_fini(void)) {
81  /* We can only use signal-safe functions here.
82  * That includes read/write and close, fortunately.
83  * We do all of this directly here instead of resetting
84  * uv__signal_global_init_guard because
85  * uv__signal_global_once_init is only called from uv_loop_init
86  * and this needs to function in existing loops.
87  */
88  if (uv__signal_lock_pipefd[0] != -1) {
90  uv__signal_lock_pipefd[0] = -1;
91  }
92 
93  if (uv__signal_lock_pipefd[1] != -1) {
95  uv__signal_lock_pipefd[1] = -1;
96  }
97 }
98 
99 
100 static void uv__signal_global_reinit(void) {
101  uv__signal_global_fini();
102 
104  abort();
105 
106  if (uv__signal_unlock())
107  abort();
108 }
109 
110 
112  uv_once(&uv__signal_global_init_guard, uv__signal_global_init);
113 }
114 
115 
116 static int uv__signal_lock(void) {
117  int r;
118  char data;
119 
120  do {
121  r = read(uv__signal_lock_pipefd[0], &data, sizeof data);
122  } while (r < 0 && errno == EINTR);
123 
124  return (r < 0) ? -1 : 0;
125 }
126 
127 
128 static int uv__signal_unlock(void) {
129  int r;
130  char data = 42;
131 
132  do {
133  r = write(uv__signal_lock_pipefd[1], &data, sizeof data);
134  } while (r < 0 && errno == EINTR);
135 
136  return (r < 0) ? -1 : 0;
137 }
138 
139 
140 static void uv__signal_block_and_lock(sigset_t* saved_sigmask) {
141  sigset_t new_mask;
142 
143  if (sigfillset(&new_mask))
144  abort();
145 
146  if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask))
147  abort();
148 
149  if (uv__signal_lock())
150  abort();
151 }
152 
153 
154 static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) {
155  if (uv__signal_unlock())
156  abort();
157 
158  if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL))
159  abort();
160 }
161 
162 
164  /* This function must be called with the signal lock held. */
167 
168  lookup.signum = signum;
169  lookup.flags = 0;
170  lookup.loop = NULL;
171 
172  handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup);
173 
174  if (handle != NULL && handle->signum == signum)
175  return handle;
176 
177  return NULL;
178 }
179 
180 
181 static void uv__signal_handler(int signum) {
184  int saved_errno;
185 
186  saved_errno = errno;
187  memset(&msg, 0, sizeof msg);
188 
189  if (uv__signal_lock()) {
190  errno = saved_errno;
191  return;
192  }
193 
194  for (handle = uv__signal_first_handle(signum);
195  handle != NULL && handle->signum == signum;
196  handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
197  int r;
198 
199  msg.signum = signum;
200  msg.handle = handle;
201 
202  /* write() should be atomic for small data chunks, so the entire message
203  * should be written at once. In theory the pipe could become full, in
204  * which case the user is out of luck.
205  */
206  do {
207  r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg);
208  } while (r == -1 && errno == EINTR);
209 
210  assert(r == sizeof msg ||
211  (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)));
212 
213  if (r != -1)
214  handle->caught_signals++;
215  }
216 
218  errno = saved_errno;
219 }
220 
221 
222 static int uv__signal_register_handler(int signum, int oneshot) {
223  /* When this function is called, the signal lock must be held. */
224  struct sigaction sa;
225 
226  /* XXX use a separate signal stack? */
227  memset(&sa, 0, sizeof(sa));
228  if (sigfillset(&sa.sa_mask))
229  abort();
230  sa.sa_handler = uv__signal_handler;
231  sa.sa_flags = SA_RESTART;
232  if (oneshot)
233  sa.sa_flags |= SA_RESETHAND;
234 
235  /* XXX save old action so we can restore it later on? */
236  if (sigaction(signum, &sa, NULL))
237  return UV__ERR(errno);
238 
239  return 0;
240 }
241 
242 
243 static void uv__signal_unregister_handler(int signum) {
244  /* When this function is called, the signal lock must be held. */
245  struct sigaction sa;
246 
247  memset(&sa, 0, sizeof(sa));
248  sa.sa_handler = SIG_DFL;
249 
250  /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a
251  * signal implies that it was successfully registered earlier, so EINVAL
252  * should never happen.
253  */
254  if (sigaction(signum, &sa, NULL))
255  abort();
256 }
257 
258 
260  int err;
261 
262  /* Return if already initialized. */
263  if (loop->signal_pipefd[0] != -1)
264  return 0;
265 
266  err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK);
267  if (err)
268  return err;
269 
270  uv__io_init(&loop->signal_io_watcher,
272  loop->signal_pipefd[0]);
273  uv__io_start(loop, &loop->signal_io_watcher, POLLIN);
274 
275  return 0;
276 }
277 
278 
280  uv__io_stop(loop, &loop->signal_io_watcher, POLLIN);
281  uv__close(loop->signal_pipefd[0]);
282  uv__close(loop->signal_pipefd[1]);
283  loop->signal_pipefd[0] = -1;
284  loop->signal_pipefd[1] = -1;
286 }
287 
288 
290  QUEUE* q;
291 
292  /* Stop all the signal watchers that are still attached to this loop. This
293  * ensures that the (shared) signal tree doesn't contain any invalid entries
294  * entries, and that signal handlers are removed when appropriate.
295  * It's safe to use QUEUE_FOREACH here because the handles and the handle
296  * queue are not modified by uv__signal_stop().
297  */
298  QUEUE_FOREACH(q, &loop->handle_queue) {
299  uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue);
300 
301  if (handle->type == UV_SIGNAL)
303  }
304 
305  if (loop->signal_pipefd[0] != -1) {
306  uv__close(loop->signal_pipefd[0]);
307  loop->signal_pipefd[0] = -1;
308  }
309 
310  if (loop->signal_pipefd[1] != -1) {
311  uv__close(loop->signal_pipefd[1]);
312  loop->signal_pipefd[1] = -1;
313  }
314 }
315 
316 
318  int err;
319 
321  if (err)
322  return err;
323 
324  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL);
325  handle->signum = 0;
326  handle->caught_signals = 0;
327  handle->dispatched_signals = 0;
328 
329  return 0;
330 }
331 
332 
335 }
336 
337 
339  return uv__signal_start(handle, signal_cb, signum, 0);
340 }
341 
342 
345  int signum) {
346  return uv__signal_start(handle, signal_cb, signum, 1);
347 }
348 
349 
352  int signum,
353  int oneshot) {
354  sigset_t saved_sigmask;
355  int err;
356  uv_signal_t* first_handle;
357 
358  assert(!uv__is_closing(handle));
359 
360  /* If the user supplies signum == 0, then return an error already. If the
361  * signum is otherwise invalid then uv__signal_register will find out
362  * eventually.
363  */
364  if (signum == 0)
365  return UV_EINVAL;
366 
367  /* Short circuit: if the signal watcher is already watching {signum} don't
368  * go through the process of deregistering and registering the handler.
369  * Additionally, this avoids pending signals getting lost in the small
370  * time frame that handle->signum == 0.
371  */
372  if (signum == handle->signum) {
373  handle->signal_cb = signal_cb;
374  return 0;
375  }
376 
377  /* If the signal handler was already active, stop it first. */
378  if (handle->signum != 0) {
380  }
381 
382  uv__signal_block_and_lock(&saved_sigmask);
383 
384  /* If at this point there are no active signal watchers for this signum (in
385  * any of the loops), it's time to try and register a handler for it here.
386  * Also in case there's only one-shot handlers and a regular handler comes in.
387  */
388  first_handle = uv__signal_first_handle(signum);
389  if (first_handle == NULL ||
390  (!oneshot && (first_handle->flags & UV_SIGNAL_ONE_SHOT))) {
391  err = uv__signal_register_handler(signum, oneshot);
392  if (err) {
393  /* Registering the signal handler failed. Must be an invalid signal. */
394  uv__signal_unlock_and_unblock(&saved_sigmask);
395  return err;
396  }
397  }
398 
399  handle->signum = signum;
400  if (oneshot)
401  handle->flags |= UV_SIGNAL_ONE_SHOT;
402 
403  RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle);
404 
405  uv__signal_unlock_and_unblock(&saved_sigmask);
406 
407  handle->signal_cb = signal_cb;
409 
410  return 0;
411 }
412 
413 
415  uv__io_t* w,
416  unsigned int events) {
419  char buf[sizeof(uv__signal_msg_t) * 32];
420  size_t bytes, end, i;
421  int r;
422 
423  bytes = 0;
424  end = 0;
425 
426  do {
427  r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes);
428 
429  if (r == -1 && errno == EINTR)
430  continue;
431 
432  if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
433  /* If there are bytes in the buffer already (which really is extremely
434  * unlikely if possible at all) we can't exit the function here. We'll
435  * spin until more bytes are read instead.
436  */
437  if (bytes > 0)
438  continue;
439 
440  /* Otherwise, there was nothing there. */
441  return;
442  }
443 
444  /* Other errors really should never happen. */
445  if (r == -1)
446  abort();
447 
448  bytes += r;
449 
450  /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */
451  end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t);
452 
453  for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) {
454  msg = (uv__signal_msg_t*) (buf + i);
455  handle = msg->handle;
456 
457  if (msg->signum == handle->signum) {
458  assert(!(handle->flags & UV_HANDLE_CLOSING));
459  handle->signal_cb(handle, handle->signum);
460  }
461 
462  handle->dispatched_signals++;
463 
464  if (handle->flags & UV_SIGNAL_ONE_SHOT)
466  }
467 
468  bytes -= end;
469 
470  /* If there are any "partial" messages left, move them to the start of the
471  * the buffer, and spin. This should not happen.
472  */
473  if (bytes) {
474  memmove(buf, buf + end, bytes);
475  continue;
476  }
477  } while (end == sizeof buf);
478 }
479 
480 
482  int f1;
483  int f2;
484  /* Compare signums first so all watchers with the same signnum end up
485  * adjacent.
486  */
487  if (w1->signum < w2->signum) return -1;
488  if (w1->signum > w2->signum) return 1;
489 
490  /* Handlers without UV_SIGNAL_ONE_SHOT set will come first, so if the first
491  * handler returned is a one-shot handler, the rest will be too.
492  */
493  f1 = w1->flags & UV_SIGNAL_ONE_SHOT;
494  f2 = w2->flags & UV_SIGNAL_ONE_SHOT;
495  if (f1 < f2) return -1;
496  if (f1 > f2) return 1;
497 
498  /* Sort by loop pointer, so we can easily look up the first item after
499  * { .signum = x, .loop = NULL }.
500  */
501  if (w1->loop < w2->loop) return -1;
502  if (w1->loop > w2->loop) return 1;
503 
504  if (w1 < w2) return -1;
505  if (w1 > w2) return 1;
506 
507  return 0;
508 }
509 
510 
512  assert(!uv__is_closing(handle));
514  return 0;
515 }
516 
517 
519  uv_signal_t* removed_handle;
520  sigset_t saved_sigmask;
521  uv_signal_t* first_handle;
522  int rem_oneshot;
523  int first_oneshot;
524  int ret;
525 
526  /* If the watcher wasn't started, this is a no-op. */
527  if (handle->signum == 0)
528  return;
529 
530  uv__signal_block_and_lock(&saved_sigmask);
531 
532  removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle);
533  assert(removed_handle == handle);
534  (void) removed_handle;
535 
536  /* Check if there are other active signal watchers observing this signal. If
537  * not, unregister the signal handler.
538  */
539  first_handle = uv__signal_first_handle(handle->signum);
540  if (first_handle == NULL) {
542  } else {
543  rem_oneshot = handle->flags & UV_SIGNAL_ONE_SHOT;
544  first_oneshot = first_handle->flags & UV_SIGNAL_ONE_SHOT;
545  if (first_oneshot && !rem_oneshot) {
546  ret = uv__signal_register_handler(handle->signum, 1);
547  assert(ret == 0);
548  (void)ret;
549  }
550  }
551 
552  uv__signal_unlock_and_unblock(&saved_sigmask);
553 
554  handle->signum = 0;
556 }
uv__is_closing
#define uv__is_closing(h)
Definition: uv-common.h:238
async_greeter_server_with_graceful_shutdown.loop
loop
Definition: async_greeter_server_with_graceful_shutdown.py:59
uv_signal_cb
void(* uv_signal_cb)(uv_signal_t *handle, int signum)
Definition: uv.h:376
memset
return memset(p, 0, total)
RB_REMOVE
#define RB_REMOVE(name, x, y)
Definition: tree.h:728
uv__signal_msg_t::signum
int signum
Definition: unix/signal.c:37
write
#define write
Definition: test-fs.c:47
xds_manager.f1
f1
Definition: xds_manager.py:42
uv__signal_stop
static void uv__signal_stop(uv_signal_t *handle)
Definition: unix/signal.c:518
string.h
uv__signal_first_handle
static uv_signal_t * uv__signal_first_handle(int signum)
Definition: unix/signal.c:163
buf
voidpf void * buf
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
error_ref_leak.err
err
Definition: error_ref_leak.py:35
uv__signal_start
static int uv__signal_start(uv_signal_t *handle, uv_signal_cb signal_cb, int signum, int oneshot)
Definition: unix/signal.c:350
uv__io_init
void uv__io_init(uv__io_t *w, uv__io_cb cb, int fd)
Definition: unix/core.c:853
QUEUE_DATA
#define QUEUE_DATA(ptr, type, field)
Definition: queue.h:30
UV__F_NONBLOCK
#define UV__F_NONBLOCK
Definition: third_party/libuv/src/unix/internal.h:291
uv__signal_handler
static void uv__signal_handler(int signum)
Definition: unix/signal.c:181
uv__signal_compare
static int uv__signal_compare(uv_signal_t *w1, uv_signal_t *w2)
Definition: unix/signal.c:481
uv__signal_loop_once_init
static int uv__signal_loop_once_init(uv_loop_t *loop)
Definition: unix/signal.c:259
uv__signal_loop_fork
int uv__signal_loop_fork(uv_loop_t *loop)
Definition: unix/signal.c:279
RB_HEAD
RB_HEAD(uv__signal_tree_s, uv_signal_s)
end
char * end
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1008
RB_NFIND
#define RB_NFIND(name, x, y)
Definition: tree.h:730
uv_signal_stop
int uv_signal_stop(uv_signal_t *handle)
Definition: unix/signal.c:511
RB_NEXT
#define RB_NEXT(name, x, y)
Definition: tree.h:731
uv_once
UV_EXTERN void uv_once(uv_once_t *guard, void(*callback)(void))
Definition: libuv/src/unix/thread.c:418
UV_SIGNAL_ONE_SHOT
@ UV_SIGNAL_ONE_SHOT
Definition: uv-common.h:121
uv__io_start
void uv__io_start(uv_loop_t *loop, uv__io_t *w, unsigned int events)
Definition: unix/core.c:870
uv__signal_event
static void uv__signal_event(uv_loop_t *loop, uv__io_t *w, unsigned int events)
Definition: unix/signal.c:414
signal_cb
static void signal_cb(uv_signal_t *signal, int signum)
Definition: test-signal-pending-on-close.c:42
uv_signal_start
int uv_signal_start(uv_signal_t *handle, uv_signal_cb signal_cb, int signum)
Definition: unix/signal.c:338
uv__make_pipe
int uv__make_pipe(int fds[2], int flags)
Definition: unix/process.c:142
uv__signal_lock
static int uv__signal_lock(void)
Definition: unix/signal.c:116
uv__signal_msg_t
Definition: unix/signal.c:35
uv_once_t
pthread_once_t uv_once_t
Definition: unix.h:133
UV__ERR
#define UV__ERR(x)
Definition: errno.h:29
RB_INSERT
#define RB_INSERT(name, x, y)
Definition: tree.h:727
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
uv__signal_msg_t::handle
uv_signal_t * handle
Definition: unix/signal.c:36
uv__handle_init
#define uv__handle_init(loop_, h, type_)
Definition: uv-common.h:284
msg
std::string msg
Definition: client_interceptors_end2end_test.cc:372
uv__signal_global_once_init
void uv__signal_global_once_init(void)
Definition: unix/signal.c:111
uv__signal_unlock_and_unblock
static void uv__signal_unlock_and_unblock(sigset_t *saved_sigmask)
Definition: unix/signal.c:154
uv_signal_start_oneshot
int uv_signal_start_oneshot(uv_signal_t *handle, uv_signal_cb signal_cb, int signum)
Definition: unix/signal.c:343
uv.h
uv_signal_init
int uv_signal_init(uv_loop_t *loop, uv_signal_t *handle)
Definition: unix/signal.c:317
UV_HANDLE_CLOSING
@ UV_HANDLE_CLOSING
Definition: uv-common.h:66
uv__signal_global_reinit
static void uv__signal_global_reinit(void)
Definition: unix/signal.c:100
uv_signal_s
Definition: uv.h:1561
internal.h
read
int read(izstream &zs, T *x, Items items)
Definition: bloaty/third_party/zlib/contrib/iostream2/zstream.h:115
uv__signal_register_handler
static int uv__signal_register_handler(int signum, int oneshot)
Definition: unix/signal.c:222
bytes
uint8 bytes[10]
Definition: bloaty/third_party/protobuf/src/google/protobuf/io/coded_stream_unittest.cc:153
UV_DESTRUCTOR
UV_DESTRUCTOR(static void uv__signal_global_fini(void))
Definition: unix/signal.c:80
uv__signal_lock_pipefd
static int uv__signal_lock_pipefd[2]
Definition: unix/signal.c:57
SA_RESTART
#define SA_RESTART
Definition: unix/signal.c:32
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
uv__signal_loop_cleanup
void uv__signal_loop_cleanup(uv_loop_t *loop)
Definition: unix/signal.c:289
fix_build_deps.r
r
Definition: fix_build_deps.py:491
QUEUE_FOREACH
#define QUEUE_FOREACH(q, h)
Definition: queue.h:36
uv__signal_unregister_handler
static void uv__signal_unregister_handler(int signum)
Definition: unix/signal.c:243
lookup
static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v, uint32_t hash, eqlfunc_t *eql)
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/upb.c:1417
uv__signal_global_init_guard
static uv_once_t uv__signal_global_init_guard
Definition: unix/signal.c:54
uv__io_stop
void uv__io_stop(uv_loop_t *loop, uv__io_t *w, unsigned int events)
Definition: unix/core.c:898
uv_signal_s::signum
int signum
Definition: uv.h:1564
UV_ONCE_INIT
#define UV_ONCE_INIT
Definition: unix.h:131
handle
static csh handle
Definition: test_arm_regression.c:16
uv__signal_block_and_lock
static void uv__signal_block_and_lock(sigset_t *saved_sigmask)
Definition: unix/signal.c:140
uv_handle_s
Definition: uv.h:441
xds_manager.f2
f2
Definition: xds_manager.py:85
uv_loop_s
Definition: uv.h:1767
uv__handle_start
#define uv__handle_start(h)
Definition: uv-common.h:241
uv__signal_close
void uv__signal_close(uv_signal_t *handle)
Definition: unix/signal.c:333
uv__io_s
Definition: unix.h:94
uv__signal_tree
static struct uv__signal_tree_s uv__signal_tree
Definition: unix/signal.c:55
uv__handle_stop
#define uv__handle_stop(h)
Definition: uv-common.h:249
RB_INITIALIZER
#define RB_INITIALIZER(root)
Definition: tree.h:301
RB_GENERATE_STATIC
RB_GENERATE_STATIC(uv__signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare)
Definition: unix/signal.c:59
errno.h
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
uv__signal_unlock
static int uv__signal_unlock(void)
Definition: unix/signal.c:128
QUEUE
void * QUEUE[2]
Definition: queue.h:21
uv__close
int uv__close(int fd)
Definition: unix/core.c:557


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:18