2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-09-04 08:15:37 +00:00
Commit Graph

2223 Commits

Author SHA1 Message Date
Radostin Stoyanov
90cbeadb66 zdtm: Replace if->continue with if->elif->else
Replacing the if->continue pattern with if->elif->else
reduces the number of lines while preserving the logic.

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2020-02-04 12:39:04 -08:00
Nidhi Gupta
389bcfef3e test/java: Add FileRead Tests
Signed-off-by: Nidhi Gupta <itsnidhi16@gmail.com>
2020-02-04 12:39:04 -08:00
Vitaly Ostrosablin
c4006c0034 test/static:conntracks: Support nftables
Update test to support both iptables and nft to create conntrack rules.

Signed-off-by: Vitaly Ostrosablin <vostrosablin@virtuozzo.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:39:04 -08:00
Ashutosh Mehra
00ce121fd5 Add criu to PATH env variable in libcriu tests
PATH is pointing to incorrect location for `criu` executable
causing libcriu tests to fail when running in travis.
Also added statements to display log file contents on failure
to help in debugging.

Signed-off-by: Ashutosh Mehra <asmehra1@in.ibm.com>
2020-02-04 12:39:04 -08:00
Ashutosh Mehra
f8125b8bef Couple of fixes to build and run libcriu tests
libcriu tests are currently broken. This patch fixes couple of
issues to allow the building and running libcriu tests.

1. lib/c/criu.h got updated to include version.h which is present
at "criu/include", but the command to compile libcriu tests is not
specifying "criu/include" in the path to be searched for header
files. This resulted in compilation error.
This can be fixed by adding "-I ../../../../../criu/criu/include"
however it causes more problems as "criu/include/fcntl.h" would now
hide system defined fcntl.h
Solution is to use "-iquote ../../../../../criu/criu/include"
which applies only to the quote form of include directive.

2. Secondly, libcriu.so major version got updated to 2 but
libcriu/run.sh still assumes verion 1. Instead of just updating the
version in libcriu/run.sh to 2, this patch updates the libcriu/Makefile
to use "CRIU_SO_VERSION_MAJOR" so that future changes to major version
of libcriu won't cause same problem again.

Signed-off-by: Ashutosh Mehra <asmehra1@in.ibm.com>
2020-02-04 12:39:04 -08:00
Andrei Vagin
3efe44382f image: avoid name conflicts in image files
Conflict register for file "sk-opts.proto": READ is already defined in
file "rpc.proto". Please fix the conflict by adding package name on the
proto file, or use different name for the duplication.  Note: enum
values appear as siblings of the enum type instead of children of it.

https://github.com/checkpoint-restore/criu/issues/815
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:39:04 -08:00
Andrei Vagin
d305576996 zdtm: handle --pre-dump-mode in the rpc mode
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:39:04 -08:00
Abhishek Dubey
20d4920a8b Adding --pre-dump-mode option
Two modes of pre-dump algorithm:
    1) splicing memory by parasite
        --pre-dump-mode=splice (default)
    2) using process_vm_readv syscall
        --pre-dump-mode=read

Signed-off-by: Abhishek Dubey <dubeyabhishek777@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:39:02 -08:00
Andrei Vagin
f44939317f zdtm/cgroup_yard: create a test cgroup yard from the post-start hook
Right now, it is created from the pre-dump hook, but
if the --snap option is set, the test fails:
$ python test/zdtm.py run -t zdtm/static/cgroup_yard -f h --snap --iter 3
...
Running zdtm/static/cgroup_yard.hook(--pre-dump)
Traceback (most recent call last):
  File zdtm/static/cgroup_yard.hook, line 14, in <module>
    os.mkdir(yard)
OSError: [Errno 17] File exists: 'external_yard'

Cc: Michał Cłapiński <mclapinski@google.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:38:24 -08:00
Andrei Vagin
db40ef5be6 test/cgroup_yard: always clean up a test cgroup yard
Right now it is cleaned up from a post-restore hook,
but zdtm.py can be executed with the norst option:
$ zdtm.py run -t zdtm/static/cgroup_yard --norst
...
OSError: [Errno 17] File exists: 'external_yard'

Cc: Michał Cłapiński <mclapinski@google.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2020-02-04 12:38:24 -08:00
Michał Cłapiński
cf0080505a test: implement test for new --cgroup-yard option
Signed-off-by: Michał Cłapiński <mclapinski@google.com>
2020-02-04 12:37:37 -08:00
Radostin Stoyanov
6b615ca152 test/others: Reuse setup_swrk()
Reduce code duplication by taking setup_swrk() function into a separate
module that can be reused in multiple places.

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:57 +03:00
Radostin Stoyanov
4c1ee3e227 test/other: Resolve Py3 compatibility issues
When Python 2 is not installed we assume that /usr/bin/python refers to
version 3 of Python and the executable /usr/bin/python2 does not exist.

This commit also resolves a compatibility issue with Popen where in
Py2 file descriptors will be inherited by the child process and in
Py3 they will be closed by default.

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:56 +03:00
Andrei Vagin
5aa72e7237 py: Reformat everything into pep8 style
As discussed on the mailing list, current .py files formatting does not
conform to the world standard, so we should better reformat it. For this
the yapf tool is used. The command I used was

  yapf -i $(find -name *.py)

Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
2019-09-07 15:59:56 +03:00
Pavel Tikhomirov
5ff4fcb753 zdtm: make inotify04 require restore
After adding the test for fake inotify events cleanup on restore, we've
detected that we also have the same problem on dump/predump, criu
touches files that are watched and generates fake events:

  [root@snorch criu]# test/zdtm.py run -t zdtm/static/inotify04 --norst -k always
  === Run 1/1 ================ zdtm/static/inotify04
  ======================== Run zdtm/static/inotify04 in h ========================
  Start test
  ./inotify04 --pidfile=inotify04.pid --outfile=inotify04.out --dirname=inotify04.test
  Run criu dump
  =[log]=> dump/zdtm/static/inotify04/36/1/dump.log
  ------------------------ grep Error ------------------------
  (00.004050) fsnotify: 			openable (inode match) as home/snorch/devel/criu/test/zdtm/static/inotify04.test/inotify-testfile
  (00.004052) fsnotify: 	Dumping /home/snorch/devel/criu/test/zdtm/static/inotify04.test/inotify-testfile as path for handle
  (00.004055) fsnotify: id 0x000007 flags 0x000800
  (00.004071) 36 fdinfo 5: pos:                0 flags:             4000/0
  (00.004080) Warn  (criu/fsnotify.c:336): fsnotify: The 0x000008 inotify events will be dropped
  ------------------------ ERROR OVER ------------------------
  Send the 15 signal to  36
  Wait for zdtm/static/inotify04(36) to die for 0.100000
  ############### Test zdtm/static/inotify04 FAIL at result check ################
  Test output: ================================
  18:20:10.558:    36: Event       0x20
  18:20:10.558:    36: Event       0x10
  18:20:10.558:    36: Event       0x20
  18:20:10.558:    36: Event       0x10
  18:20:10.558:    36: Event       0x20
  18:20:10.558:    36: Event       0x10
  18:20:10.558:    36: Event       0x20
  18:20:10.558:    36: Event       0x10
  18:20:10.558:    36: Read 8 events
  18:20:10.558:    36: FAIL: inotify04.c:105: Found 8 unexpected inotify events (errno = 11 (Resource temporarily unavailable))

   <<< ================================
  ##################################### FAIL #####################################

To suppress fails in jenkins make the inotify04 test 'reqrst'. Still
need to cleanup (or do not create) these events on dump/predump.
2019-09-07 15:59:56 +03:00
Andrei Vagin
5f91f920a8 test: bring the lo interface up in each network namespace
This is needed to workaround the problem with "ip route save":
(00.113153) 	Running ip route save
Error: ipv4: FIB table does not exist.

Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:56 +03:00
Pavel Tikhomirov
e5bdcbbd1d zdtm/inotify: add a test that no unexpected events appear after c/r
Just create two inotify watches on a testfile, and do nothing except
c/r, it is expected that there is no events in queue after these.

before "inotify: cleanup auxiliary events from queue":

[root@snorch criu]# ./test/zdtm.py run -t zdtm/static/inotify04
=== Run 1/1 ================ zdtm/static/inotify04
======================== Run zdtm/static/inotify04 in h ========================
 DEP       inotify04.d
 CC        inotify04.o
 LINK      inotify04
Start test
./inotify04 --pidfile=inotify04.pid --outfile=inotify04.out --dirname=inotify04.test
Run criu dump
Run criu restore
Send the 15 signal to  60
Wait for zdtm/static/inotify04(60) to die for 0.100000
=============== Test zdtm/static/inotify04 FAIL at result check ================
Test output: ================================
18:37:14.279:    60: Event       0x10
18:37:14.280:    60: Event       0x20
18:37:14.280:    60: Event       0x10
18:37:14.280:    60: Read 3 events
18:37:14.280:    60: FAIL: inotify04.c:105: Found 3 unexpected inotify events (errno = 11 (Resource temporarily unavailable))

<<< ================================

v2: make two inotifies on the same file

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>

zdtm: inotify04 add another inotify on the same file
2019-09-07 15:59:56 +03:00
Michał Cłapiński
6606f246c2 Add ZDTM tests for child subreaper property
1. Basic check if property is migrated
2. Check that property is restored for existing children
3. Check that child subreaper does not affect reparenting

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Michał Cłapiński <mclapinski@google.com>
Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
2019-09-07 15:59:54 +03:00
Andrei Vagin
a82275f3d4 zdtm: use a proper page size for the host
In zdtm.py, the page size is hardcoded as 4096, but on ppc64le, is is equal
to 64K and all test fail with errors like this:

ERROR: bad page counts, stats = 13 real = 208(0)

Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:54 +03:00
Andrei Vagin
cb6768b62c test/packet_sock_mmap: parse inode as unsigned long long
7f95a16df000-7f95a16e1000 rw-p 00000000 00:09 2183152397                 socket:[2183152397]

Reported-by: Mr Jenkins
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:54 +03:00
Pavel Tikhomirov
3ca4c73e47 zdtm: make grep_errors also grep warnings
It is inspired by the discussion about inotify fix:
https://github.com/checkpoint-restore/criu/pull/728#issuecomment-506929427

From one point of view, warnings might be important to understand why we
detect some visible change in the environment after c/r-ing the process,
and if this change is expected or not. So we should add "Warn" messages
to the output.

From over point, these warnings if they are expected, can spoil our
final logs with a lot of unnecessary details, so add changes in previous
patches to silence the most noisy of these warnings.

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
2019-09-07 15:59:54 +03:00
Radostin Stoyanov
b12d4f2758 zdtm: Add --tls option
Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:53 +03:00
Radostin Stoyanov
bd7920717f zdtm: Fix memory and resource leaks
These errors were found by Cppcheck 1.84

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:53 +03:00
Dmitry Safonov
a96f0f1877 util-vdso: Check chain for STN_UNDEF
Rather than chain[chain] != STN_UNDEF.
Seems like, on !ARM32 vdso there are more symbols and less chance to hit
this "feature".

Fixes parsing of __vdso_clock_gettime symbol on v5.1 arm kernel.

Signed-off-by: Dmitry Safonov <dima@arista.com>
2019-09-07 15:59:52 +03:00
Pavel Emelianov
33bc00a158 zdtm: Check stats file presence before reading
In some cases the stats-dump file can be missing, so do not
crash the whole zdtm.py in this case.

https://ci.openvz.org/job/CRIU/job/criu-live-migration/job/criu-dev/2362/console

Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
2019-09-07 15:59:52 +03:00
Andrei Vagin
31c3f3bc1b test/s390: add a new patch to xtables libraries
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:52 +03:00
Pavel Emelianov
b336fa2e32 zdtm: Check pages stats after dump
After dump command -- verify that the amount of bytes counted in
stats-dump matches the real sizes of pages-*.img files.

Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:52 +03:00
Radostin Stoyanov
5af2bd905f zdtm: Add UDP broadcast test
Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:49 +03:00
Radostin Stoyanov
8e59ed48bd zdtm: Simplify string to boolean conversion
The built-in bool() function returns a boolean value by converting the
input using standard truth testing procedure.

https://docs.python.org/3/library/functions.html#bool

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-09-07 15:59:49 +03:00
Harshavardhan Unnibhavi
9c9d151693 test/exhaustive: Replace map by list comprehension
Fixes #331.

https://github.com/checkpoint-restore/criu/issues/331
Signed-off-by: Harshavardhan Unnibhavi <hvubfoss@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Dmitry Safonov
8c5b25cbf5 zdtm/thread-bomb: Limit stack size in thread-bomb
ia32 thread-bomb test failed when compel refused to seize the test,
trying to mmap() in remote process and getting ENOMEM.
It turns to be true - remote process thread-bomb was filled with 8Mb
mappings created by pthread_create() (the default stack size).

So, that 1024 * 8Mb is a bit too much to place in 4Gb.

Fix the test on 32-bit platforms by using much smaller stack.
Also check the return value of pthread_create().

Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Dmitry Safonov
a0662df6f9 zdtm/vdso/ia32: Ignore vsyscall page appear
Not a major bummer.
On the other side, it's also becomes less important as it seems that
distribution switches from LEGACY_VSYSCALL_EMULATE to
LEGACY_VSYSCALL_NONE (by security reasons).
Might be not worth fixing at all in the end.

Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Dmitry Safonov
3b579373c1 zdtm/vdso/ia32: Use uint64_t for /proc/self/maps
Add some comments to state things those might be not obvious.

Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Rikard Falkeborn
d04eba411f test/bers: Fix sizeof to memset
sizeof(fd) is the size of the pointer. Make sure the entire array is set
by using the number of elements times the size of the elements.

Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Rikard Falkeborn
e95b5c67ae test: add missing va_end
Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:49 +03:00
Rikard Falkeborn
bdf8972b61 test: remove unused variables
Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:48 +03:00
Rikard Falkeborn
66846b26a0 test/zdtm: Move assignment after return value check
If read() fails we can not use the return value as index. Move the use
of it to after the error check to avoid this.

Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:48 +03:00
Cyrill Gorcunov
bd4a52e82e test: socket_udplite -- Test shudowned sockets
Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-09-07 15:59:48 +03:00
Dmitry Safonov
6d66dd5d89 zdtm/ia32: fcntl() wrapper for old glibc(s)
A bit nasty, but does the job to run ofd tests on glibc < v2.28.
Other way would be to update glibc on Travis-CI ia32 tests, but
I thought someone might want to run the tests outside Travis-CI.

Fixes: #745

Signed-off-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-07-18 20:23:56 +03:00
Radostin Stoyanov
2a3e34155c zdtm: Refactor seccomp_filter_{threads,tsync}
As discusses on the musl mailing list [1] when libc api is used to
create a POSIX thread, and this thread is killed by seccomp, this
breaks a fundamental assumption the C runtime relies on, causing
any libc call (i.e. pthread_join) after the kill to have undefined
behaviour.

In order to work around the issue we could use SECCOMP_RET_ERRNO
instead of SECCOMP_RET_KILL. This filter will set a magic value
to user space as errno without executing the system call.

[1] https://www.openwall.com/lists/musl/2019/06/26/7

Rresolves #725

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-07-17 12:48:50 -07:00
Adrian Reber
a1cf51841a selinux: add socket label test
This adds two more SELinux test to verfy that checkpointing and
restoring SELinux socket labels works correctly, if the process uses
setsockcreatecon() or if the process leaves the default context for
newly created sockets.

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-05-16 09:39:30 -07:00
Adrian Reber
149fed6480 test: Verify that sockcreate does not change during restore
This makes sure that sockcreate stays empty for selinux00 before and
after checkpoint/restore.

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-05-16 09:39:30 -07:00
Andrei Vagin
5412247572 arm: fix atomic_{add,sub}_return
Now, this code doesn't pass this simple test:

        atomic_t a;;
        int c = 0;
        atomic_set(&a, c);
        c = atomic_inc(&a);
        if (c != 0) {
                pr_err("c = %x &a = %p\n", c, &a);
                return 1;
        }

08:28:51.771: 26187: ERR: fd.c:36: c = 7ef47a60 &a = 0x7ef47a60

Reported-by: Mr Jenkins
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-04-25 20:23:18 +03:00
Zhang Ning
f4fa53cf9b Android NDK: __errno is already defined as a function
criu/log.c:356:16: error: called object type 'int' is not a function or function pointer
        int __errno = errno;
                      ^~~~~
/root/android-ndk/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/include/errno.h:43:24: note: expanded from macro 'errno'
                ~~~~~~~^
criu/log.c:391:2: error: called object type 'int' is not a function or function pointer
        errno =  __errno;
        ^~~~~
/root/android-ndk/toolchains/llvm/prebuilt/linux-x86_64//sysroot/usr/include/errno.h:43:24: note: expanded from macro 'errno'

in Android NDK's errno.h:

42: int* __errno(void) __attribute_const__;
43: #define errno (*__errno())

so rename __errno to _errno to pass build

Cc: Chen Hu <hu1.chen@intel.com>
Signed-off-by: Zhang Ning <ning.a.zhang@intel.com>
Reviewed-by: Cyrill Gorcunov <gorcunov@gmail.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-04-20 20:25:26 -07:00
Radostin Stoyanov
e65c7b55e9 zdtm: Replace imp module with importlib
imp.load_source() has been deprecated [1]. The recommended alternative
API for loading a module is exec_module() [2].

[1] https://docs.python.org/2.7/library/imp.html#imp.load_module
[2] https://docs.python.org/3.4/library/importlib.html#importlib.abc.Loader.exec_module

Signed-off-by: Radostin Stoyanov <rstoyanov1@gmail.com>
2019-04-20 20:25:26 -07:00
Adrian Reber
2844b95f67 test: add selinux00 test
This tests if CRIU can restore a process with the same policy as during
checkpointing.

The test selinux00 is started and if SELinux is available the test
process moves itself to another process context. To make this possible
either a new SELinux policy needs to be available containing:

2d537cabbb

Or for a short time SELinux is switched to permissive mode.

The correct SELinux setup is done by zdtm/static/selinux00.checkskip and
zdtm/static/selinux00.hook and after the test the previous SELinux
policy state is restored.

After the test case is restored the test case checks if it still has the
same SELinux process context as before. If not the test cases fails.

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-04-20 20:25:26 -07:00
Mitul Karnik
861afa0bc7 Removing Unneeded Return Value Assignments
Removed return value assignment statements as they are not referenced or used
anywhere after the assignment is done.

Fixes #334: Removing Unneeded Assignments

Signed-off-by: Mitul Karnik <mitulkarnik.92@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
2019-04-20 20:25:26 -07:00
Ashutosh Mehra
9f176a9686 Access pathname relative to root of mntns
Use faccessat() in check_path_remap() to check if the file (relative
to root of mnt ns) is accessible or not.

Signed-off-by: Ashutosh Mehra <asmehra1@in.ibm.com>
2019-04-20 20:25:26 -07:00
Adrian Reber
31f3a6a737 test: fix compilation error
binfmt_misc.c:168:23: error: ‘sprintf’ may write a terminating nul past the end of the destination [-Werror=format-overflow=]
  168 |   sprintf(path, "%s/%s", dirname, NAME[i]);
      |                       ^

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-04-20 20:25:26 -07:00
Adrian Reber
a256500185 Rename version protobuf RPC members
In rpc.proto the interface to query the CRIU version number uses major
and minor as keywords. This creates errors when using the RPC
definitions with C++: https://github.com/checkpoint-restore/criu/issues/625

In this commit the fields are renamed from major to major_number and
from minor to minor_number.

For existing programs using the RPC protobuf definition this should be a
transparent change. Only for programs importing the latest rpc.proto it
will require code changes.

Signed-off-by: Adrian Reber <areber@redhat.com>
2019-04-20 20:25:26 -07:00