summaryrefslogtreecommitdiffstats
path: root/tests/deckard/contrib/libfaketime/src/faketime.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/deckard/contrib/libfaketime/src/faketime.c')
-rw-r--r--tests/deckard/contrib/libfaketime/src/faketime.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/tests/deckard/contrib/libfaketime/src/faketime.c b/tests/deckard/contrib/libfaketime/src/faketime.c
new file mode 100644
index 0000000..037d749
--- /dev/null
+++ b/tests/deckard/contrib/libfaketime/src/faketime.c
@@ -0,0 +1,385 @@
+/*
+ * libfaketime wrapper command
+ *
+ * This file is part of libfaketime, version 0.9.7
+ *
+ * libfaketime is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * libfaketime is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License v2 along
+ * with the libfaketime; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Converted from shell script by Balint Reczey with the following credits
+ * and comments:
+ *
+ * Thanks to Daniel Kahn Gillmor for improvement suggestions.
+
+ * This wrapper exposes only a small subset of the libfaketime functionality.
+ * Please see libfaketime's README file and man page for more details.
+
+ * Acknowledgment: Parts of the functionality of this wrapper have been
+ * inspired by Matthias Urlichs' datefudge 1.14.
+
+ * Compile time configuration: Path where the libfaketime libraries can be found
+ * on Linux/UNIX
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <semaphore.h>
+
+#include "faketime_common.h"
+
+const char version[] = "0.9.7";
+
+#ifdef __APPLE__
+static const char *date_cmd = "gdate";
+#else
+static const char *date_cmd = "date";
+#endif
+
+#define PATH_BUFSIZE 4096
+
+/* semaphore and shared memory names */
+char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0};
+
+void usage(const char *name)
+{
+ printf("\n"
+ "Usage: %s [switches] <timestamp> <program with arguments>\n"
+ "\n"
+ "This will run the specified 'program' with the given 'arguments'.\n"
+ "The program will be tricked into seeing the given 'timestamp' as its starting date and time.\n"
+ "The clock will continue to run from this timestamp. Please see the manpage (man faketime)\n"
+ "for advanced options, such as stopping the wall clock and make it run faster or slower.\n"
+ "\n"
+ "The optional switches are:\n"
+ " -m : Use the multi-threaded version of libfaketime\n"
+ " -f : Use the advanced timestamp specification format (see manpage)\n"
+ " --exclude-monotonic : Prevent monotonic clock from drifting (not the raw monotonic one)\n"
+ "\n"
+ "Examples:\n"
+ "%s 'last friday 5 pm' /bin/date\n"
+ "%s '2008-12-24 08:15:42' /bin/date\n"
+ "%s -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
+ "%s -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
+ "%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do date; sleep 1 ; done'\n"
+ "In this single case all spawned processes will use the same global clock\n"
+ "without restarting it at the start of each process.\n\n"
+ "(Please note that it depends on your locale settings whether . or , has to be used for fractions)\n"
+ "\n", name, name, name, name, name, name);
+}
+
+/** Clean up shared objects */
+static void cleanup_shobjs()
+{
+ if (-1 == sem_unlink(sem_name))
+ {
+ perror("sem_unlink");
+ }
+ if (-1 == shm_unlink(shm_name))
+ {
+ perror("shm_unlink");
+ }
+}
+
+int main (int argc, char **argv)
+{
+ pid_t child_pid;
+ int curr_opt = 1;
+ bool use_mt = false, use_direct = false;
+ long offset;
+
+ while(curr_opt < argc)
+ {
+ if (0 == strcmp(argv[curr_opt], "-m"))
+ {
+ use_mt = true;
+ curr_opt++;
+ continue;
+ }
+ else if (0 == strcmp(argv[curr_opt], "-f"))
+ {
+ use_direct = true;
+ curr_opt++;
+ continue;
+ }
+ else if (0 == strcmp(argv[curr_opt], "--exclude-monotonic"))
+ {
+ setenv("DONT_FAKE_MONOTONIC", "1", true);
+ curr_opt++;
+ continue;
+ }
+ else if ((0 == strcmp(argv[curr_opt], "-v")) ||
+ (0 == strcmp(argv[curr_opt], "--version")))
+ {
+ printf("\n%s: Version %s\n"
+ "For usage information please use '%s --help'.\n",
+ argv[0], version, argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ else if ((0 == strcmp(argv[curr_opt], "-h")) ||
+ (0 == strcmp(argv[curr_opt], "-?")) ||
+ (0 == strcmp(argv[curr_opt], "--help")))
+ {
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ else
+ {
+ /* we parsed all options */
+ break;
+ }
+ }
+
+ /* we need at least a timestamp string and a command to run */
+ if (argc - curr_opt < 2)
+ {
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!use_direct)
+ {
+ // TODO get seconds
+ int pfds[2];
+ (void) (pipe(pfds) + 1);
+ int ret = EXIT_SUCCESS;
+
+ if (0 == (child_pid = fork()))
+ {
+ close(1); /* close normal stdout */
+ (void) (dup(pfds[1]) + 1); /* make stdout same as pfds[1] */
+ close(pfds[0]); /* we don't need this */
+ if (EXIT_SUCCESS != execlp(date_cmd, date_cmd, "-d", argv[curr_opt], "+%s",(char *) NULL))
+ {
+ perror("Running (g)date failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ char buf[256] = {0}; /* e will have way less than 256 digits */
+ close(pfds[1]); /* we won't write to this */
+ (void) (read(pfds[0], buf, 256) + 1);
+ waitpid(child_pid, &ret, 0);
+ if (ret != EXIT_SUCCESS)
+ {
+ printf("Error: Timestamp to fake not recognized, please re-try with a "
+ "different timestamp.\n");
+ exit(EXIT_FAILURE);
+ }
+ offset = atol(buf) - time(NULL);
+ ret = snprintf(buf, sizeof(buf), "%s%ld", (offset >= 0)?"+":"", offset);
+ setenv("FAKETIME", buf, true);
+ close(pfds[0]); /* finished reading */
+ }
+ }
+ else
+ {
+ /* simply pass format string along */
+ setenv("FAKETIME", argv[curr_opt], true);
+ }
+ int keepalive_fds[2];
+ (void) (pipe(keepalive_fds) + 1);
+
+ /* we just consumed the timestamp option */
+ curr_opt++;
+
+ {
+ /* create semaphores and shared memory */
+ int shm_fd;
+ sem_t *sem;
+ struct ft_shared_s *ft_shared;
+ char shared_objs[PATH_BUFSIZE * 2 + 1];
+
+ /*
+ * Casting of getpid() return value to long needed to make GCC on SmartOS
+ * happy, since getpid's return value's type on SmartOS is long. Since
+ * getpid's return value's type is int on most other systems, and that
+ * sizeof(long) always >= sizeof(int), this works on all platforms without
+ * the need for crazy #ifdefs.
+ */
+ snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%ld", (long)getpid());
+ snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%ld", (long)getpid());
+
+ if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
+ {
+ perror("sem_open");
+ exit(EXIT_FAILURE);
+ }
+
+ /* create shm */
+ if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
+ {
+ perror("shm_open");
+ if (-1 == sem_unlink(argv[2]))
+ {
+ perror("sem_unlink");
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /* set shm size */
+ if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
+ {
+ perror("ftruncate");
+ cleanup_shobjs();
+ exit(EXIT_FAILURE);
+ }
+
+ /* map shm */
+ if (MAP_FAILED == (ft_shared = mmap(NULL, sizeof(struct ft_shared_s), PROT_READ|PROT_WRITE,
+ MAP_SHARED, shm_fd, 0)))
+ {
+ perror("mmap");
+ cleanup_shobjs();
+ exit(EXIT_FAILURE);
+ }
+
+ if (sem_wait(sem) == -1)
+ {
+ perror("sem_wait");
+ cleanup_shobjs();
+ exit(EXIT_FAILURE);
+ }
+
+ /* init elapsed time ticks to zero */
+ ft_shared->ticks = 0;
+ ft_shared->file_idx = 0;
+ ft_shared->start_time.real.tv_sec = 0;
+ ft_shared->start_time.real.tv_nsec = -1;
+ ft_shared->start_time.mon.tv_sec = 0;
+ ft_shared->start_time.mon.tv_nsec = -1;
+ ft_shared->start_time.mon_raw.tv_sec = 0;
+ ft_shared->start_time.mon_raw.tv_nsec = -1;
+
+ if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
+ {
+ perror("munmap");
+ cleanup_shobjs();
+ exit(EXIT_FAILURE);
+ }
+
+ if (sem_post(sem) == -1)
+ {
+ perror("semop");
+ cleanup_shobjs();
+ exit(EXIT_FAILURE);
+ }
+
+ snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
+ setenv("FAKETIME_SHARED", shared_objs, true);
+ sem_close(sem);
+ }
+
+ {
+ char *ftpl_path;
+#ifdef __APPLE__
+ ftpl_path = PREFIX "/libfaketime.1.dylib";
+ FILE *check;
+ check = fopen(ftpl_path, "ro");
+ if (check == NULL)
+ {
+ ftpl_path = PREFIX "/lib/faketime/libfaketime.1.dylib";
+ }
+ else
+ {
+ fclose(check);
+ }
+ setenv("DYLD_INSERT_LIBRARIES", ftpl_path, true);
+ setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", true);
+#else
+ {
+ char *ld_preload_new, *ld_preload = getenv("LD_PRELOAD");
+ size_t len;
+ if (use_mt)
+ {
+ /*
+ * on MultiArch platforms, such as Debian, we put a literal $LIB into LD_PRELOAD.
+ */
+#ifndef MULTI_ARCH
+ ftpl_path = PREFIX LIBDIRNAME "/libfaketimeMT.so.1";
+#else
+ ftpl_path = PREFIX "/$LIB/faketime/libfaketimeMT.so.1";
+#endif
+ }
+ else
+ {
+#ifndef MULTI_ARCH
+ ftpl_path = PREFIX LIBDIRNAME "/libfaketime.so.1";
+#else
+ ftpl_path = PREFIX "/$LIB/faketime/libfaketime.so.1";
+#endif
+ }
+ len = ((ld_preload)?strlen(ld_preload) + 1: 0) + 1 + strlen(ftpl_path);
+ ld_preload_new = malloc(len);
+ snprintf(ld_preload_new, len ,"%s%s%s", (ld_preload)?ld_preload:"",
+ (ld_preload)?":":"", ftpl_path);
+ setenv("LD_PRELOAD", ld_preload_new, true);
+ free(ld_preload_new);
+ }
+#endif
+ }
+
+ /* run command and clean up shared objects */
+ if (0 == (child_pid = fork()))
+ {
+ close(keepalive_fds[0]); /* only parent needs to read this */
+ if (EXIT_SUCCESS != execvp(argv[curr_opt], &argv[curr_opt]))
+ {
+ perror("Running specified command failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ int ret;
+ char buf;
+ close(keepalive_fds[1]); /* only children need keep this open */
+ waitpid(child_pid, &ret, 0);
+ (void) (read(keepalive_fds[0], &buf, 1) + 1); /* reads 0B when all children exit */
+ cleanup_shobjs();
+ if (WIFSIGNALED(ret))
+ {
+ fprintf(stderr, "Caught %s\n", strsignal(WTERMSIG(ret)));
+ exit(EXIT_FAILURE);
+ }
+ exit(WEXITSTATUS(ret));
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Editor modelines
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 2
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=2 expandtab:
+ * :indentSize=2:tabSize=2:noTabs=true:
+ */
+
+/* eof */