Commit 5a49b9e2 authored by Andrey Vagin's avatar Andrey Vagin Committed by Cyrill Gorcunov

zdtm: Zero Downtime Migration Test Suite

This test suite contains many small test cases for different subsystems.

Example of execution:
 # make busyloop00.pid
 # ../../../../crtools -d -t `cat busyloop00.pid`
 # kill -9 `cat busyloop00.pid`
 # ../../../../crtools -r -t `cat busyloop00.pid`
 # cat busyloop00.out
PASS
Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Acked-by: 's avatarPavel Emelyanov <xemul@parallels.com>
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
parent 315b7981
SUBDIRS = lib live
default: all
.PHONY: default
%:
set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
CFLAGS = -g -O2 -Wall
LIB = libzdtmtst.a
LIBSRC = datagen.c msg.c parseargs.c test.c streamutil.c
LIBOBJ = $(LIBSRC:%.c=%.o)
LIBDEP = $(LIBSRC:%.c=%.d)
DEPEND.c = $(COMPILE.c) -MM -MP
%.d: %.c
$(DEPEND.c) $(OUTPUT_OPTION) $<
all: $(LIB)
install: all
$(LIB): $(LIB)(${LIBOBJ})
clean:
$(RM) $(LIBOBJ) $(LIB) *~
cleandep:
$(RM) $(LIBDEP)
realclean: clean cleandep
.PHONY: force clean cleandep cleanout realclean start check_start stop wait_stop
-include $(LIBDEP)
#include <stdlib.h>
#include "zdtmtst.h"
/* update CRC-32 */
#define CRCPOLY 0xedb88320
static inline uint32_t crc32_le8(uint32_t crc, uint8_t datum)
{
int i;
crc ^= datum;
for (i = 0; i < 8; i++)
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0);
return crc;
}
void datagen(uint8_t *buffer, unsigned length, uint32_t *crc)
{
uint32_t rnd = 0;
unsigned shift;
for (shift = 0; length-- > 4; buffer++, shift--, rnd >>= 8) {
if (!shift) {
shift = 4;
rnd = mrand48();
}
*buffer = rnd;
if (crc)
*crc = crc32_le8(*crc, *buffer);
}
if (crc) {
*buffer++ = *crc;
*buffer++ = *crc >> 8;
*buffer++ = *crc >> 16;
*buffer++ = *crc >> 24;
}
}
int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc)
{
uint32_t read_crc;
for (; length-- > 4; buffer++)
*crc = crc32_le8(*crc, *buffer);
read_crc = buffer[0] |
buffer[1] << 8 |
buffer[2] << 16 |
buffer[3] << 24;
if (read_crc != *crc) {
test_msg("Read: %x, Expected: %x\n", read_crc, *crc);
return 1;
}
return 0;
}
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include "zdtmtst.h"
static struct {
char buffer[LOG_BUF_SIZE];
char *ptr;
int left;
} msg_buf = {
.buffer = {},
.ptr = msg_buf.buffer,
.left = sizeof(msg_buf.buffer),
};
void test_msg(const char *format, ...)
{
va_list arg;
int len;
va_start(arg, format);
len = vsnprintf(msg_buf.ptr, msg_buf.left, format, arg);
va_end(arg);
if (len >= msg_buf.left) { /* indicate message buffer overflow */
const static char overflow_mark[] = "\n.@.\n";
msg_buf.left = 0;
msg_buf.ptr = msg_buf.buffer + sizeof(msg_buf.buffer);
strcpy(msg_buf.ptr - sizeof(overflow_mark), overflow_mark);
msg_buf.ptr--; /* correct for terminating '\0' */
return;
}
msg_buf.ptr += len;
msg_buf.left -= len;
}
extern int proc_id;
void dump_msg(const char *fname)
{
if (msg_buf.ptr != msg_buf.buffer) {
int fd;
if (proc_id == 0) {
fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644);
} else {
char fname_child[1000];
snprintf(fname_child,1000,"%s.%d",fname,proc_id);
fd = open(fname_child, O_WRONLY | O_CREAT, 0644);
}
if (fd < 0)
return;
/* ignore errors as there's no way to report them */
write(fd, msg_buf.buffer, msg_buf.ptr - msg_buf.buffer);
out:
close(fd);
}
}
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "zdtmtst.h"
static struct long_opt *opt_head;
static int help;
TEST_OPTION(help, bool, "print help message and exit", 0);
void __push_opt(struct long_opt *opt)
{
opt->next = opt_head;
/* FIXME: barrier ? */
opt_head = opt;
}
int parse_opt_bool(char *param, void *arg)
{
if (param == NULL ||
!strcmp(param, "on") ||
!strcmp(param, "yes") ||
!strcmp(param, "true")) {
* (int *) arg = 1;
return 0;
}
if (!strcmp(param, "off") ||
!strcmp(param, "no") ||
!strcmp(param, "false")) {
* (int *) arg = 0;
return 0;
}
return -EINVAL;
}
int parse_opt_int(char *param, void *arg)
{
char *tail;
if (param == NULL || param[0] == '\0')
return -EINVAL;
* (int *) arg = strtol(param, &tail, 0);
if (tail[0] != '\0')
return -EINVAL;
return 0;
}
int parse_opt_uint(char *param, void *arg)
{
char *tail;
if (param == NULL || param[0] == '\0')
return -EINVAL;
* (unsigned int *) arg = strtoul(param, &tail, 0);
if (tail[0] != '\0')
return -EINVAL;
return 0;
}
int parse_opt_long(char *param, void *arg)
{
char *tail;
if (param == NULL || param[0] == '\0')
return -EINVAL;
* (long *) arg = strtol(param, &tail, 0);
if (tail[0] != '\0')
return -EINVAL;
return 0;
}
int parse_opt_ulong(char *param, void *arg)
{
char *tail;
if (param == NULL || param[0] == '\0')
return -EINVAL;
* (unsigned long *) arg = strtoul(param, &tail, 0);
if (tail[0] != '\0')
return -EINVAL;
return 0;
}
int parse_opt_string(char *param, void *arg)
{
if (param == NULL || param[0] == '\0')
return -EINVAL;
* (char **) arg = param;
return 0;
}
static void printopt(const struct long_opt *opt)
{
const char *obracket = "", *cbracket = "";
if (!opt->is_required) {
obracket = "[";
cbracket = "]";
}
fprintf(stderr, " %s--%s=%s%s\t%s\n",
obracket, opt->name, opt->type, cbracket, opt->doc);
}
static void helpexit(void)
{
struct long_opt *opt;
fputs("Usage:\n", stderr);
for (opt = opt_head; opt; opt = opt->next)
printopt(opt);
exit(1);
}
const char *test_doc;
const char *test_author;
static void prdoc(void)
{
if (test_doc)
fprintf(stderr, "%s\n", test_doc);
if (test_author)
fprintf(stderr, "Author: %s\n", test_author);
}
void parseargs(int argc, char ** argv)
{
int i;
struct long_opt *opt;
for (i = 1; i < argc; i++) {
char *name, *value;
if (strlen(argv[i]) < 2 || strncmp(argv[i], "--", 2)) {
fprintf(stderr, "%s: options should start with --\n", argv[i]);
helpexit();
}
name = argv[i] + 2;
value = strchr(name, '=');
if (value)
*value++ = '\0';
for (opt = opt_head; opt; opt = opt->next)
if (!strcmp(name, opt->name)) {
if (opt->parse_opt(value, opt->value)) {
fprintf(stderr, "%s: failed to parse\n", argv[i]);
helpexit();
}
else
/* -1 marks fulfilled requirement */
opt->is_required = - opt->is_required;
break;
}
if (!opt) {
fprintf(stderr, "%s: unknown option\n", argv[i]);
helpexit();
}
}
if (help) {
prdoc();
helpexit();
}
for (opt = opt_head; opt; opt = opt->next)
if (opt->is_required > 0) {
fprintf(stderr, "mandatory flag --%s not given\n", opt->name);
helpexit();
}
}
#!/bin/bash
#
# parse command line flags of the form --foo=bar and print out an eval-able line
name=$0
function die() {
echo "$name: $*" >&2
exit 1
}
# eat our flags first
while : ; do
flag=$1
shift || break
case $flag in
--flags-req=*) # req'd flags
oIFS="$IFS" IFS=","
vars_req=(${flag#*=})
IFS="$oIFS"
;;
--flags-opt=*) # optional flags
oIFS="$IFS" IFS=","
vars_opt=(${flag#*=})
IFS="$oIFS"
;;
--name=*) # name to report errors as
name=${flag#*=}
;;
--flags-only) # report only flags
show_flags=true show_args=false
;;
--no-flags) # report only remaining args
show_flags=false show_args=true
;;
--) # end of our flags; external flags follow
break
;;
esac
done
# consume external flags
while : ; do
flag=$1
shift || break
case $flag in
--*=*)
;;
--) # end of external flags; uninterpreted arguments follow
break
;;
*) # pass unrecognized arguments through
args="$args '$flag'"
continue
;;
esac
flagname=${flag%%=*}
flagname=${flagname#--}
flagval=${flag#*=}
# check if this flag is declared
case " ${vars_req[*]} ${vars_opt[*]} " in
*" $flagname "*)
;;
*) # pass unrecognized flags through
args="$args '$flag'"
continue
;;
esac
eval $flagname=\"$flagval\"
done
# check that we have all required flags
for var in ${vars_req[@]}; do
${!var+true} die "--$var is required"
done
# now print 'em out
if ${show_flags:-true}; then
for var in ${vars_req[@]} ${vars_opt[@]}; do
# only print those that are set (even to an empty string)
${!var+echo $var="'${!var}'"}
done
fi
if ${show_args:-true}; then
for arg in "$@"; do # get quotes right
args="$args '$arg'"
done
echo "set -- $args"
fi
#!/bin/bash
export PATH=$PATH:${0%/*}
function die() {
echo "ERR: $*"
exit 1
}
tmpargs="$(parseargs.sh --name=$0 --flags-req=pidfile,outfile -- "$@")" ||
die "can't parse command line"
eval "$tmpargs"
# check that pidfile exists
if [ ! -r "$pidfile" ]; then
# if the testcase has written out the outfile, print it
if [ -r "$outfile" ]; then
echo $(< "$outfile")
exit 1
else
die "pidfile $pidfile doesn't exist"
fi
fi
# try to stop the testcase
kill -TERM $(< $pidfile)
# wait at most this many sec for the testcase to stop and wipe out the pidfile
declare -i loops=10
while [ -f "$pidfile" ]; do
((loops--)) || die "$pidfile still exists"
sleep 1
done
# see if the testcase has written out the result file
[ -f "$outfile" ] || die "$outfile doesn't exist"
# read in the result
res="$(< "$outfile")"
# dump it to stdout, with the return code reflecting the status
case "$res" in
PASS)
echo "$res"
exit 0
;;
FAIL:* | ERR:*)
echo "$res"
exit 1
;;
*)
die "$outfile is incomprehensible"
;;
esac
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "zdtmtst.h"
int set_nonblock(int fd, int on)
{
int flag;
flag = fcntl(fd, F_GETFL, 0);
if (flag < 0)
return flag;
if (on)
flag |= O_NONBLOCK;
else
flag &= ~O_NONBLOCK;
return fcntl(fd, F_SETFL, flag);
}
int pipe_in2out(int infd, int outfd, uint8_t *buffer, int length)
{
uint8_t *buf;
int rlen, wlen;
while (1) {
rlen = read(infd, buffer, length);
if (rlen <= 0)
return rlen;
/* don't go reading until we're done with writing */
for (buf = buffer; rlen > 0; buf += wlen, rlen -= wlen) {
wlen = write(outfd, buf, rlen);
if (wlen < 0)
return wlen;
}
}
}
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "zdtmtst.h"
static volatile sig_atomic_t sig_received = 0;
static void sig_hand(int signo)
{
sig_received = signo;
}
static char *outfile;
TEST_OPTION(outfile, string, "output file", 1);
static char *pidfile;
TEST_OPTION(pidfile, string, "file to store pid", 1);
static pid_t master_pid = 0;
int proc_id = 0;
static int proc_id_cur = 0;
int test_fork() {
proc_id_cur++;
pid_t pid = fork();
if (pid == 0)
proc_id = proc_id_cur;
return pid;
}
static void test_fini(void)
{
extern void dump_msg(const char *);
dump_msg(outfile);
if (getpid() == master_pid)
unlink(pidfile);
}
static void setup_outfile()
{
if (!access(outfile, F_OK) || errno != ENOENT) {
fprintf(stderr, "Output file %s appears to exist, aborting\n",
outfile);
exit(1);
}
if (atexit(test_fini)) {
fprintf(stderr, "Can't register exit function\n");
exit(1);
}
}
static void redir_stdfds()
{
int nullfd;
nullfd = open("/dev/null", O_RDWR);
if (nullfd < 0) {
err("Can't open /dev/null: %m\n");
exit(1);
}
dup2(nullfd, 0);
dup2(nullfd, 1);
dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
void test_init(int argc, char **argv)
{
extern void parseargs(int, char **);
pid_t pid;
static FILE *pidf;
struct sigaction sa = {
.sa_handler = sig_hand,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGTERM, &sa, NULL)) {
fprintf(stderr, "Can't set SIGTERM handler: %m\n");
exit(1);
}
if (sigaction(SIGCHLD, &sa, NULL)) {
fprintf(stderr, "Can't set SIGCHLD handler: %m\n");
exit(1);
}
parseargs(argc, argv);
setup_outfile();
redir_stdfds();
pidf = fopen(pidfile, "wx");
if (!pidf) {
err("Can't create pid file %s: %m\n", pidfile);
exit(1);
}
pid = fork();
if (pid < 0) {
err("Daemonizing failed: %m\n");
exit(1);
}
if (pid) { /* parent will exit when the child is ready */
test_waitsig();
if (sig_received == SIGCHLD) {
int ret;
waitpid(pid, &ret, 0);
if (WIFEXITED(ret)) {
err("Test exited with unexpectedly with code %d\n", WEXITSTATUS(ret));
exit(0);
}
if (WIFSIGNALED(ret)) {
err("Test exited on unexpected signal %d\n", WTERMSIG(ret));
exit(0);
}
}
fprintf(pidf, "%d\n", pid);
fclose(pidf);
_exit(0);
}
/* record the test pid to remember the ownership of the pidfile */
master_pid = getpid();
fclose(pidf);
sa.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &sa, NULL)) {
err("Can't reset SIGCHLD handler: %m\n");
exit(1);
}
if (setsid() < 0) {
err("Can't become session group leader: %m\n");
exit(1);
}
srand48(time(NULL)); /* just in case we need it */
}
void test_daemon()
{
pid_t ppid;
ppid = getppid();
if (ppid <= 1) {
err("Test orphaned\n");
goto out;
}
if (kill(ppid, SIGTERM))
goto out;
return;
out:
/* kill out our process group for safety */
kill(0, SIGKILL);
exit(1);
}
int test_go(void)
{
return !sig_received;
}
void test_waitsig(void)
{
sigset_t mask, oldmask;
/* Set up the mask of signals to temporarily block. */
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGCHLD);
/* Wait for a signal to arrive. */
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while (!sig_received)
sigsuspend (&oldmask);
sigprocmask (SIG_UNBLOCK, &mask, NULL);
}
#ifndef _VIMITESU_H_
#define _VIMITESU_H_
#include <sys/types.h>
/* set up test */
extern void test_init(int argc, char **argv);
/* finish setting up the test, write out pid file, and go to background */
extern void test_daemon(void);
/* store a message to a static buffer */
extern void test_msg(const char *format, ...);
/* tell if SIGUSR1 hasn't been received yet */
extern int test_go(void);
/* sleep until SIGUSR1 is delivered */
extern void test_waitsig(void);
/* dump test status and messages, wipe out pid file, etc. */
extern void test_fini(void);
#include <stdint.h>
/* generate data with crc32 at the end of the buffer */
extern void datagen(uint8_t *buffer, unsigned length, uint32_t *crc);
/* check the data buffer against its crc32 */
extern int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc);
/* streaming helpers */
extern int set_nonblock(int fd, int on);
extern int pipe_in2out(int infd, int outfd, uint8_t *buffer, int length);
/* command line args */
struct long_opt {
const char *name;
const char *type;
const char *doc;
int is_required;
int (*parse_opt)(char *arg, void *value);
void *value;
struct long_opt *next;
};
extern void __push_opt(struct long_opt *opt);
#define TEST_OPTION(name, type, doc, is_required) \
param_check_##type(name, &(name)); \
static struct long_opt __long_opt_##name = { \
#name, #type, doc, is_required, parse_opt_##type, &name }; \
static void __init_opt_##name(void) __attribute__ ((constructor)); \
static void __init_opt_##name(void) { __push_opt(&__long_opt_##name); }
#define __param_check(name, p, type) \
static inline type *__check_##name(void) { return(p); }
extern int parse_opt_bool(char *param, void *arg);
#define param_check_bool(name, p) __param_check(name, p, int)
extern int parse_opt_int(char *param, void *arg);
#define param_check_int(name, p) __param_check(name, p, int)
extern int parse_opt_uint(char *param, void *arg);
#define param_check_uint(name, p) __param_check(name, p, unsigned int)
extern int parse_opt_long(char *param, void *arg);
#define param_check_long(name, p) __param_check(name, p, long)
extern int parse_opt_ulong(char *param, void *arg);
#define param_check_ulong(name, p) __param_check(name, p, unsigned long)
extern int parse_opt_string(char *param, void *arg);
#define param_check_string(name, p) __param_check(name, p, char *)
#include <stdio.h>
#include <errno.h>
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
/* message helpers */
#define err(format, arg...) \
test_msg("ERR: %s:%d: " format " (errno = %d)\n", \
__FILE__, __LINE__, ## arg, errno)
#define fail(format, arg...) \
test_msg("FAIL: %s:%d: " format " (errno = %d)\n", \
__FILE__, __LINE__, ## arg, errno)
#define pass() test_msg("PASS\n")
#endif /* _VIMITESU_H_ */
#ifndef _VIMITESU_H_
#define _VIMITESU_H_
#include <sys/types.h>
/* set up test */
extern void test_init(int argc, char **argv);
/*wrapper for fork: init log offset*/
extern int test_fork();
/* finish setting up the test, write out pid file, and go to background */
extern void test_daemon(void);
/* store a message to a static buffer */
extern void test_msg(const char *format, ...);
/* tell if SIGTERM hasn't been received yet */
extern int test_go(void);
/* sleep until SIGTERM is delivered */
extern void test_waitsig(void);
#include <stdint.h>
/* generate data with crc32 at the end of the buffer */
extern void datagen(uint8_t *buffer, unsigned length, uint32_t *crc);
/* check the data buffer against its crc32 */
extern int datachk(const uint8_t *buffer, unsigned length, uint32_t *crc);
/* streaming helpers */
extern int set_nonblock(int fd, int on);
extern int pipe_in2out(int infd, int outfd, uint8_t *buffer, int length);
/* command line args */
struct long_opt {
const char *name;
const char *type;
const char *doc;
int is_required;
int (*parse_opt)(char *arg, void *value);
void *value;
struct long_opt *next;
};
extern void __push_opt(struct long_opt *opt);
#define TEST_OPTION(name, type, doc, is_required) \
param_check_##type(name, &(name)); \
static struct long_opt __long_opt_##name = { \
#name, #type, doc, is_required, parse_opt_##type, &name }; \
static void __init_opt_##name(void) __attribute__ ((constructor)); \
static void __init_opt_##name(void) { __push_opt(&__long_opt_##name); }
#define __param_check(name, p, type) \
static inline type *__check_##name(void) { return(p); }
extern int parse_opt_bool(char *param, void *arg);
#define param_check_bool(name, p) __param_check(name, p, int)
extern int parse_opt_int(char *param, void *arg);
#define param_check_int(name, p) __param_check(name, p, int)
extern int parse_opt_uint(char *param, void *arg);
#define param_check_uint(name, p) __param_check(name, p, unsigned int)
extern int parse_opt_long(char *param, void *arg);
#define param_check_long(name, p) __param_check(name, p, long)
extern int parse_opt_ulong(char *param, void *arg);
#define param_check_ulong(name, p) __param_check(name, p, unsigned long)
extern int parse_opt_string(char *param, void *arg);
#define param_check_string(name, p) __param_check(name, p, char *)
#include <stdio.h>
#include <errno.h>
#define __stringify_1(x) #x
#define __stringify(x) __stringify_1(x)
/* message helpers */
#define LOG_BUF_SIZE 0x1000
#define err(format, arg...) \
test_msg("ERR: %s:%d: " format " (errno = %d)\n", \
__FILE__, __LINE__, ## arg, errno)
#define fail(format, arg...) \
test_msg("FAIL: %s:%d: " format " (errno = %d)\n", \
__FILE__, __LINE__, ## arg, errno)
#define skip(format, arg...) \
test_msg("SKIP: %s:%d: " format "\n", \
__FILE__, __LINE__, ## arg)
#define pass() test_msg("PASS\n")
#endif /* _VIMITESU_H_ */
SUBDIRS = streaming transition static
default: all
.PHONY: default
%:
set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
LIBDIR = ../../lib
LIB = $(LIBDIR)/libzdtmtst.a
override CPPFLAGS += -I$(LIBDIR)
CFLAGS = -g -O2 -Wall
TST_NOFILE = \
busyloop00 \
sleeping00 \
pid00 \
wait00 \
zombie00 \
fpu00 \
futex \
mmx00 \
sse00 \
sse20 \
mprotect00 \
timers \
unbound_sock \
socket_aio \
msgque \
inotify_system \
inotify_system_nodel \
shm \
ptrace_sig \
# jobctl00 \
TST_FILE = \
write_read00 \
write_read01 \
write_read02 \
write_read10 \
maps00 \
link10 \
file_attr \
deleted_unix_sock \
deleted_dev \
unlink_fstat00 \
unlink_fstat01 \
unlink_largefile \
mtime_mmap \
fifo \
fifo_wronly \
unlink_fifo \
unlink_fifo_wronly \
TST_DIR = \
cwd00 \
overmount_dev \
overmount_file \
overmount_fifo \
overmount_sock \
TST = \
$(TST_NOFILE) \
$(TST_FILE) \
$(TST_DIR) \
env00 \
umask00 \
TST_STATE = \
conntracks \
route_rules \
SRC = $(TST:%=%.c)
OBJ = $(SRC:%.c=%.o)
DEP = $(SRC:%.c=%.d)
PID = $(TST:%=%.pid)
OUT = $(TST:%=%.out)
STATE = $(TST_STATE:%=%.state)
STATE_OUT = $(TST_STATE:%=%.out)
DEPEND.c = $(COMPILE.c) -MM -MP
%.d: %.c
$(DEPEND.c) $(OUTPUT_OPTION) $<
all: $(TST)
install: all
inotify_system_nodel.c: inotify_system.c
ln -s inotify_system.c inotify_system_nodel.c
$(TST_NOFILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out
$(TST_FILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --filename=$<.test
$(TST_DIR:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --dirname=$<.test
env00.pid: env00
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --envname=ENV_00_TEST
umask00.pid: umask00
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --mask=0345
%.out: %.pid %
-kill -TERM `< $<`
$(TST_STATE:%=%.state): %.state: %
$(<D)/$(<F) --statefile=$@ --outfile=$<.out start
$(TST_STATE:%=%.out): %.out: % %.state
-$(<D)/$(<F) --statefile=$<.state --outfile=$@ stop
start: $(PID) $(STATE)
%.is_running: %.pid
kill -0 `< $<`
check_start: $(PID:%.pid=%.is_running)
stop: $(STATE_OUT)
-kill -TERM `cat *.pid`
WAIT_TIME=10
wait_stop:
-for ((i = 0; i < $(WAIT_TIME); i++)); do \
kill -0 `cat *.pid 2>/dev/null` 2>/dev/null || break; \
sleep 1; \
done
$(TST): $(LIB)
futex.o: override CFLAGS += -pthread
futex: override LDFLAGS += -pthread
jobctl00: override LDLIBS += -lutil
socket_aio: override LDLIBS += -lrt
unlink_largefile: override CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
inotify_system_nodel: override CFLAGS += -DNODEL
$(LIB): force
$(MAKE) -C $(LIBDIR)
clean:
$(RM) $(OBJ) $(TST) *~
cleandep: clean
$(RM) $(DEP)
cleanout:
$(RM) -r *.pid *.out* *.test* *.state
realclean: cleandep cleanout
.PHONY: force clean cleandep cleanout realclean start check_start stop wait_stop
-include $(DEP)
#include "zdtmtst.h"
const char *test_doc = "Run busy loop while migrating";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
int main(int argc, char ** argv)
{
test_init(argc, argv);
test_daemon();
while (test_go())
;
pass();
return 0;
}
#!/bin/bash
export PATH=$PATH:${0%/*}/../../lib
die()
{
echo "$0:${BASH_LINENO[0]}: $*" >&2
exit 1
}
fail()
{
echo "FAIL: $0:${BASH_LINENO[0]}: $*" > "$outfile"
exit 1
}
do_or_fail()
{
local failmsg="$1" output
shift
output="$(eval $@ 2>&1)" ||
fail "$failmsg: $output"
}
do_start()
{
[ -f "$statefile" ] && die "state file $statefile aleady exists"
do_or_fail "can't install a state match" \
iptables -A INPUT \
-m state --state RELATED,ESTABLISHED -j ACCEPT
do_or_fail "can't list the loaded iptables" \
iptables -L \> "$statefile"
}
do_stop()
{
do_or_fail "can't compare the iptables" \
iptables -L \| diff -u "$statefile" -
rm -f "$statefile"
echo "PASS" > $outfile
}
tmpargs="$(parseargs.sh --name=$0 \
--flags-req=statefile,outfile \
--flags-opt="start,stop" -- "$@")" ||
die "can't parse command line"
eval "$tmpargs"
[ -f "$outfile" ] && die "out file $outfile aleady exists"
# expect "start" or "stop"
do_$1
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Check that cwd didn't change";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
int main(int argc, char **argv)
{
char cwd0[256], cwd1[256], cwd2[256];
test_init(argc, argv);
if (!getcwd(cwd0, sizeof(cwd0))) {
err("can't get cwd: %m\n");
exit(1);
}
if (mkdir(dirname, 0700)) {
err("can't make directory %s: %m\n", dirname);
exit(1);
}
if (chdir(dirname)) {
err("can't change directory to %s: %m\n", dirname);
goto cleanup;
}
if (!getcwd(cwd1, sizeof(cwd1))) {
err("can't get cwd: %m\n");
goto cleanup;
}
test_daemon();
test_waitsig();
if (!getcwd(cwd2, sizeof(cwd2))) {
fail("can't get cwd: %m\n");
goto out;
}
if (strcmp(cwd1, cwd2))
fail("%s != %s\n", cwd1, cwd2);
else
pass();
out:
chdir(cwd0); /* return to the initial dir before writing out results */
cleanup:
chdir(cwd0);
rmdir(dirname);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a device special file "
"open and unlinked before migration";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char **argv)
{
int fd;
struct stat st;
/* /dev/null params - sure to exist in a VPS */
mode_t mode = S_IFCHR | 0700;
dev_t dev = makedev(1, 3);
test_init(argc, argv);
if (mknod(filename, mode, dev)) {
err("can't make device file \"%s\": %m\n", filename);
exit(1);
}
fd = open(filename, O_RDWR);
if (fd < 0) {
err("can't open %s: %m\n", filename);
goto out;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m", filename);
goto out;
}
test_daemon();
test_waitsig();
if (fstat(fd, &st) < 0) {
fail("can't stat %s: %m", filename);
goto out;
}
if (st.st_mode != mode || st.st_rdev != dev) {
fail("%s is no longer the device file we had");
goto out;
}
if (close(fd) < 0) {
fail("can't close %s: %m", filename);
goto out;
}
if (unlink(filename) != -1 || errno != ENOENT) {
fail("file %s should have been deleted before migration: unlink: %m\n");
goto out;
}
pass();
out:
close(fd);
unlink(filename);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "zdtmtst.h"
const char *test_doc = "Create a unix socket, and destroy it before "
"migration; check that the child can write to it "
"and the parent can read from it after migration";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
static int fill_sock_name(struct sockaddr_un *name, const char *filename)
{
if (strlen(filename) >= sizeof(name->sun_path))
return -1;
name->sun_family = AF_LOCAL;
strcpy(name->sun_path, filename);
return 0;
}
static int setup_srv_sock(void)
{
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0) {
err("filename \"%s\" is too long", filename);
return -1;
}
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
err("can't create socket: %m");
return -1;
}
if (bind(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
err("can't bind to socket \"%s\": %m", filename);
goto err;
}
if (listen(sock, 1) < 0) {
err("can't listen on a socket \"%s\": %m\n", filename);
goto err;
}
return sock;
err:
close(sock);
return -1;
}
static int setup_clnt_sock(void)
{
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0)
return -1;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0)
return -1;
if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0)
goto err;
return sock;
err:
close(sock);
return -1;
}
int main(int argc, char ** argv)
{
int sock, acc_sock, ret;
pid_t pid;
uint32_t crc;
uint8_t buf[1000];
test_init(argc, argv);
sock = setup_srv_sock();
if (sock < 0)
exit(1);
pid = test_fork();
if (pid < 0) {
err("can't fork: %m\n");
exit(1);
}
if (pid == 0) { /* child writes to the unlinked socket and returns */
close(sock);
sock = setup_clnt_sock();
if (sock < 0)
_exit(1);
test_waitsig();
crc = ~0;
datagen(buf, sizeof(buf), &crc);
if (write(sock, buf, sizeof(buf)) != sizeof(buf)) {
err("can't write to socket: %m");
exit(errno);
}
close(sock);
exit(0);
}
acc_sock = accept(sock, NULL, NULL);
if (acc_sock < 0) {
err("can't accept() the connection on \"%s\": %m", filename);
goto out_kill;
}
close(sock);
sock = acc_sock;
if (unlink(filename)) {
err("can't unlink %s: %m\n", filename);
goto out_kill;
}
test_daemon();
test_waitsig();
if (kill(pid, SIGTERM)) {
fail("terminating the child failed: %m\n");
goto out;
}
if (wait(&ret) != pid) {
fail("wait() returned wrong pid %d: %m\n", pid);
goto out;
}
if (WIFEXITED(ret)) {
ret = WEXITSTATUS(ret);
if (ret) {
fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
goto out;
}
}
if (WIFSIGNALED(ret)) {
fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
goto out;
}
if (read(sock, buf, sizeof(buf)) != sizeof(buf)) {
fail("can't read %s: %m\n", filename);
goto out;
}
crc = ~0;
if (datachk(buf, sizeof(buf), &crc)) {
fail("CRC mismatch\n");
goto out;
}
if (close(sock)) {
fail("close failed: %m\n");
goto out;
}
if (unlink(filename) != -1 || errno != ENOENT) {
fail("file %s should have been deleted before migration: unlink: %m\n");
goto out;
}
pass();
out_kill:
kill(pid, SIGTERM);
out:
close(sock);
return 0;
}
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Check that environment didn't change";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
char *envname;
TEST_OPTION(envname, string, "environment variable name", 1);
int main(int argc, char **argv)
{
char *env;
test_init(argc, argv);
if (setenv(envname, test_author, 1)) {
err("Can't set env var \"%s\" to \"%s\": %m\n", envname, test_author);
exit(1);
}
test_daemon();
test_waitsig();
env = getenv(envname);
if (!env) {
fail("can't get env var \"%s\": %m\n", envname);
goto out;
}
if (strcmp(env, test_author))
fail("%s != %s\n", env, test_author);
else
pass();
out:
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a named pipe "
"open";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char **argv)
{
int fd;
struct stat st;
mode_t mode = S_IFIFO | 0700;
test_init(argc, argv);
if (mknod(filename, mode, 0)) {
err("can't make fifo \"%s\": %m\n", filename);
exit(1);
}
fd = open(filename, O_RDWR);
if (fd < 0) {
err("can't open %s: %m\n", filename);
return 1;
}
test_daemon();
test_waitsig();
if (close(fd) < 0) {
fail("can't close %s: %m", filename);
return 1;
}
if (stat(filename, &st) < 0) {
fail("can't stat %s: %m", filename);
return 1;
}
if (st.st_mode != mode) {
fail("%s is no longer the fifo we had", filename);
return 1;
}
if (unlink(filename) < 0) {
fail("can't unlink %s: %m", filename);
return 1;
}
pass();
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a named pipe, "
"opened in WRONLY mode";
#define BUF_SIZE 256
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char **argv)
{
int fd, fd1;
struct stat st;
mode_t mode = S_IFIFO | 0600;
int pid;
int chret;
test_init(argc, argv);
if (mknod(filename, mode, 0)) {
err("can't make fifo \"%s\": %m\n", filename);
exit(1);
}
pid = test_fork();
if (pid < 0) {
err("Can't fork");
exit(1);
}
if (pid == 0) {
char rbuf[BUF_SIZE];
int res;
fd1 = open(filename, O_RDONLY);
if (fd1 < 0) {
err("open(%s, O_RDONLY) Failed: %m\n", filename);
chret = errno;
return chret;
}
res = read(fd1, rbuf, 7);
if (res < 0) {
err("read error %s: %m\n", filename);
chret = errno;
return chret;
}
else if (res == 0) {
err("read(%d, rbuf, 7) return 0", fd1);
return 1;
}
if (close(fd1) < 0) {
fail("can't close %d, %s: %m", fd1, filename);
chret = errno;
return chret;
}
} else {
fd = open(filename, O_WRONLY);
if (fd < 0) {
err("open(%s, O_WRONLY) Failed: %m\n", filename);
kill(pid, SIGKILL);
wait(NULL);
return 1;
}
test_daemon();
test_waitsig();
if (write(fd, "string", 7) == -1) {
err("write(%d, 'string', 7) Failed: %m\n", fd);
return 1;
}
wait(&chret);
chret = WEXITSTATUS(chret);
if (chret) {
fail("child exited with non-zero code %d (%s)\n",
chret, strerror(chret));
return 1;
}
if (close(fd) < 0) {
fail("can't close %d, %s: %m", fd, filename);
return 1;
}
if (stat(filename, &st) < 0) {
fail("can't stat %s: %m", filename);
return 1;
}
if (st.st_mode != mode) {
fail("%s is no longer the fifo we had", filename);
return 1;
}
if (unlink(filename) < 0) {
fail("can't unlink %s: %m", filename);
return 1;
}
}
pass();
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <utime.h>
#include <sys/stat.h>
#include "zdtmtst.h"
const char *test_doc = "Check that attributes and content of an open, "
"written to, and then unlinked file migrate "
"correctly";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
#define DEF_PERMS 06604 /* -rwS--Sr--, really esoteric one */
unsigned int perms = DEF_PERMS;
TEST_OPTION(perms, uint, "permissions to set on file "
"(default " __stringify(DEF_PERMS) ")", 0);
#define DEF_MTIME 123456 /* another really esoteric one */
unsigned int mtime = DEF_MTIME;
TEST_OPTION(mtime, uint, "mtime to set on file "
"(default " __stringify(DEF_MTIME) ")", 0);
int main(int argc, char ** argv)
{
int fd;
struct utimbuf ut;
uint32_t crc;
struct stat st;
uint8_t buf[1000000];
test_init(argc, argv);
fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
crc = ~0;
datagen(buf, sizeof(buf), &crc);
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
err("can't write to %s: %m", filename);
exit(1);
}
ut = (struct utimbuf) {
.actime = 0,
.modtime = mtime,
};
if (utime(filename, &ut)) {
err("can't set modtime %d on %s: %m\n", mtime, filename);
exit(1);
}
if (fchmod(fd, perms)) {
err("can't set perms %o on %s: %m\n", perms, filename);
exit(1);
}
if (unlink(filename)) {
err("can't unlink %s: %m\n", filename);
exit(1);
}
test_daemon();
test_waitsig();
if (lseek(fd, 0, SEEK_SET) < 0) {
fail("lseeking to the beginning of file failed: %m\n");
goto out;
}
if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
fail("can't read %s: %m\n", filename);
goto out;
}
crc = ~0;
if (datachk(buf, sizeof(buf), &crc)) {
fail("CRC mismatch\n");
goto out;
}
if (fstat(fd, &st) < 0) {
fail("can't fstat %s: %m", filename);
goto out;
}
if ((st.st_mode & 07777) != perms) {
fail("permissions have changed");
goto out;
}
if (st.st_mtime != mtime) {
fail("modification time has changed");
goto out;
}
if (close(fd)) {
fail("close failed: %m\n");
goto out_noclose;
}
if (unlink(filename) != -1 || errno != ENOENT) {
fail("file %s should have been deleted before migration: unlink: %m\n");
goto out_noclose;
}
pass();
out:
close(fd);
out_noclose:
return 0;
}
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Start a calculation, leaving FPU in a certain state,\n"
"before migration, continue after";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#if defined(__i386__) || defined(__x86_64__)
void start(float a, float b, float c, float d)
{
__asm__ volatile (
"fld %0\n"
"fadd %1\n"
"fld %2\n"
"fadd %3\n"
"fmulp %%st(1)\n"
:
: "m" (a), "m" (b), "m" (c), "m" (d)
);
}
float finish(void)
{
float res;
__asm__ volatile (
"fstp %0\n"
: "=m" (res)
);
return res;
}
int chk_proc_fpu(void)
{
unsigned long fi;
__asm__ volatile (
"mov $1, %%eax\n"
"cpuid\n"
: "=d" (fi) : : "eax"
);
return fi & (1 << 0);
}
#endif
int main(int argc, char ** argv)
{
float a, b, c, d;
float res1, res2;
test_init(argc, argv);
#if defined(__i386__) || defined(__x86_64__)
if (!chk_proc_fpu()) {
skip("FPU not supported");
return 1;
}
a = drand48();
b = drand48();
c = drand48();
d = drand48();
start(a, b, c, d);
res1 = finish();
start(a, b, c, d);
test_daemon();
test_waitsig();
res2 = finish();
if (res1 != res2)
fail("%f != %f\n", res1, res2);
else
pass();
#else
skip("Unsupported arch");
#endif
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "zdtmtst.h"
const char *test_doc = "Check (via pthread/NPTL) that futeces behave through migration";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
volatile int kid_passed;
void *thread_fn(void *lock)
{
pthread_mutex_t *mutex;
mutex = (pthread_mutex_t *)lock;
pthread_mutex_lock(mutex);
kid_passed++;
pthread_mutex_unlock(mutex);
return NULL;
}
#define DEF_NUM_THREADS 10
#define MAX_NUM_THREADS 50
int num_threads = DEF_NUM_THREADS;
TEST_OPTION(num_threads, int, "number of threads "
"(default " __stringify(DEF_NUM_THREADS)
" maximum " __stringify(MAX_NUM_THREADS) ")", 0);
int main(int argc, char **argv)
{
int i;
pthread_t thr[num_threads];
pthread_mutex_t m;
test_init(argc, argv);
if (num_threads > MAX_NUM_THREADS) {
err("%d threads it too much. max is %d\n",
num_threads, MAX_NUM_THREADS);
goto out;
}
pthread_mutex_init(&m, NULL);
pthread_mutex_lock(&m);
for (i = 0; i < num_threads; i++)
if (pthread_create(&thr[i], NULL, thread_fn, &m)) {
err("Can't create %d'th thread\n", i + 1);
goto out_kill;
}
kid_passed = 0;
test_daemon();
test_waitsig();
sleep(1);
if (kid_passed != 0)
fail("some kids broke through\n");
pthread_mutex_unlock(&m);
for (i = 0; i < num_threads; i++)
pthread_join(thr[i], NULL);
if (pthread_mutex_trylock(&m)) {
if (errno == EBUSY)
fail("kids left my mutex locked\n");
else
err("kids spoiled my mutex\n");
}
if (kid_passed != num_threads)
fail("some kids died during migration\n");
pass();
out:
return 0;
out_kill:
for (i--; i >= 0; i--) {
pthread_kill(thr[i], SIGKILL);
pthread_join(thr[i], NULL);
}
goto out;
}
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <signal.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Inotify on symlink should be checked";
#ifndef NODEL
char filename[] = "file";
char linkname[] = "file.lnk";
const char *inot_dir = "./inotify";
#else
char filename[] = "file.nodel";
char linkname[] = "file.nodel.lnk";
const char *inot_dir = "./inotify.nodel";
#endif
#ifdef __NR_inotify_init
#include <sys/inotify.h>
#ifndef IN_DONT_FOLLOW
/* Missed in SLES 10 header */
#define IN_DONT_FOLLOW 0x02000000
#endif
#define EVENT_MAX 1024
/* size of the event structure, not counting name */
#define EVENT_SIZE (sizeof (struct inotify_event))
/* reasonable guess as to size of 1024 events */
#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
#define BUF_SIZE 256
#define min_value(a,b) (a<b) ? a : b
#define handle_event(MASK) (MASK == IN_ACCESS) ? "IN_ACCESS" : \
(MASK == IN_MODIFY) ? "IN_MODIFY" : \
(MASK == IN_ATTRIB) ? "IN_ATTRIB" : \
(MASK == IN_CLOSE) ? "IN_CLOSE" : \
(MASK == IN_CLOSE_WRITE) ? "IN_CLOSE_WRITE" : \
(MASK == IN_CLOSE_NOWRITE) ? "IN_CLOSE_NOWRITE" : \
(MASK == IN_OPEN) ? "IN_OPEN" : \
(MASK == IN_MOVED_FROM) ? "IN_MOVED_FROM" : \
(MASK == IN_MOVED_TO) ? "IN_MOVED_TO" : \
(MASK == IN_DELETE) ? "IN_DELETE" : \
(MASK == IN_CREATE) ? "IN_CREATE" : \
(MASK == IN_DELETE_SELF) ? "IN_DELETE_SELF" : \
(MASK == IN_MOVE_SELF) ? "IN_MOVE_SELF" : \
(MASK == IN_UNMOUNT) ? "IN_UNMOUNT" : \
(MASK == IN_Q_OVERFLOW) ? "IN_Q_OVERFLOW" : \
(MASK == IN_IGNORED) ? "IN_IGNORED" : \
"UNKNOWN"
#include <unistd.h>
#include <fcntl.h>
typedef struct {
int inot;
uint32_t file;
uint32_t link;
uint32_t dir;
} desc;
void do_wait() {
test_daemon();
test_waitsig();
}
int createFiles(char *path, char *target, char *link) {
int fd;
fd = open(path,O_CREAT, 0644);
if (fd < 0) {
err("can't open %s: %m", path);
return -1;
}
close(fd);
if (symlink(target, link) < 0) {
err("can't symlink %s to %s: %m", path, link);
return -1;
}
return 0;
}
int addWatcher(int fd, const char *path) {
int wd;
wd = inotify_add_watch(fd, path, IN_ALL_EVENTS | IN_DONT_FOLLOW);
if (wd < 0) {
err("inotify_add_watch(%d, %s, IN_ALL_EVENTS) Failed, %s",
fd, path, strerror(errno));
return -1;
}
return wd;
}
int fChmod(char *path) {
if (chmod(path, 0755) < 0) {
err("chmod(%s, 0755) Failed, %s",
path, strerror(errno));
return -1;
}
return 0;
}
int fWriteClose(char *path) {
int fd = open(path, O_RDWR | O_CREAT, 0700);
if (fd == -1) {
err("open(%s, O_RDWR|O_CREAT,0700) Failed, %s",
path, strerror(errno));
return -1;
}
if (write(fd, "string", 7) == -1) {
err("write(%d, %s, 1) Failed, %s", fd, path, strerror(errno));
return -1;
}
if (close(fd) == -1) {
err("close(%s) Failed, %s", path, strerror(errno));
return -1;
}
return 0;
}
int fNoWriteClose(char *path) {
char buf[BUF_SIZE];
int fd = open(path, O_RDONLY);
if ( fd < 0 ) {
err("open(%s, O_RDONLY) Failed, %s",
path, strerror(errno));
return -1;
}
if (read(fd, buf, BUF_SIZE) == -1) {
err("read error: %s", strerror(errno));
close(fd);
return -1;
}
if (close(fd) == -1) {
err("close(%s) Failed, %s", path, strerror(errno));
return -1;
}
return 0;
}
int fMove(char *from, char *to) {
if (rename(from, to) == -1) {
err("rename error (from: %s to: %s) : %s",
from, to, strerror(errno));
return -1;
}
return 0;
}
desc init_env(const char *dir, char *file_path, char *link_path) {
desc in_desc = {-1, -1, -1, -1};
if (mkdir(dir, 0777) < 0) {
err("error in creating directory: %s, %s",
dir, strerror(errno));
return in_desc;
}
in_desc.inot = inotify_init();
if (in_desc.inot < 0) {
err("inotify_init () Failed, %s", strerror(errno));
rmdir(dir);
return in_desc;
}
if (snprintf(file_path, BUF_SIZE, "%s/%s", dir, filename) >= BUF_SIZE) {
err("filename %s is too long", filename);
rmdir(dir);
return in_desc;
}
if (snprintf(link_path, BUF_SIZE, "%s/%s", dir, linkname) >= BUF_SIZE) {
err("filename %s is too long", linkname);
rmdir(dir);
return in_desc;
}
in_desc.dir = addWatcher(in_desc.inot, dir);
if (createFiles(file_path, filename, link_path)) {
return in_desc;
}
in_desc.link = addWatcher(in_desc.inot, link_path);
in_desc.file = addWatcher(in_desc.inot, file_path);
return in_desc;
}
int fDelete(char *path) {
if (unlink(path) != 0) {
err("unlink: (%s)", strerror(errno));
return -1;
}
return 0;
}
int fRemDir(const char *target) {
if(rmdir(target)) {
err("rmdir: (%s)", strerror(errno));
return -1;
}
return 0;
}
int test_actions(const char *dir, char *file_path, char *link_path) {
if (
fChmod(link_path) == 0 &&
fWriteClose(link_path) == 0 &&
fNoWriteClose(link_path) == 0 &&
fMove(file_path, filename) == 0 &&
fMove(filename, file_path) == 0
#ifndef NODEL
&& fDelete(file_path) == 0 &&
fDelete(link_path) == 0 &&
fRemDir(dir) == 0
#endif
)
{
return 0;
}
return -1;
}
void dump_events(char *buf, int len) {
int marker = 0;
struct inotify_event *event;
while (marker < len) {
event = (struct inotify_event *) &buf[marker];
test_msg("\t%s (%x mask, %d len", handle_event(event->mask), event->mask, event->len);
if (event->len)
test_msg(", '%s' name", event->name);
test_msg(")\n");
marker += EVENT_SIZE + event->len;
}
}
int harmless(int mask)
{
switch (mask) {
case IN_CLOSE_NOWRITE:
case IN_ATTRIB:
return 1;
}
return 0;
}
int errors(int exp_len, int len, char *etalon_buf, char *buf) {
int marker=0;
int error=0;
while (marker < len){
struct inotify_event *event;
struct inotify_event *exp_event;
event = (struct inotify_event *) &buf[marker];
/* It's OK if some additional events are recevived */
if (marker < exp_len)
exp_event = (struct inotify_event *) &etalon_buf[marker];
else {
if (!harmless(event->mask)) {
fail("got unexpected event %s\n",
handle_event(event->mask));
error++;
}
goto next_event;
}
if (event->mask != exp_event->mask) {
fail("Handeled %s (%x mask), expected %s (%x mask)",
handle_event(event->mask), event->mask,
handle_event(exp_event->mask),
exp_event->mask);
error++;
}
if (event->len != exp_event->len) {
fail("Incorrect length of field name.");
error++;
break;
}
else if (event->len && strncmp(event->name, exp_event->name, event->len)) {
fail("Handeled file name %s, expected %s",
event->name,
exp_event->name);
error++;
}
next_event:
marker += EVENT_SIZE + event->len;
}
return error;
}
int read_set(int inot_fd, char *event_set) {
int len;
if ((len = read(inot_fd, event_set, EVENT_BUF_LEN)) < 0) {
err("read(%d, buf, %d) Failed, errno=%d : %s",
inot_fd, EVENT_BUF_LEN, errno, strerror(errno));
return -1;
}
return len;
}
void common_close(desc *descr) {
if (descr->inot > 0) {
close(descr->inot);
descr->inot=-1;
descr->file=-1;
descr->dir=-1;
descr->link=-1;
}
}
int get_event_set(char *event_set, int wait) {
int len;
char link_path[BUF_SIZE];
char file_path[BUF_SIZE];
desc common_desc;
common_desc = init_env(inot_dir, file_path, link_path);
if ((common_desc.inot < 0) || (common_desc.file < 0) || \
(common_desc.dir < 0) || (common_desc.link < 0)) {
common_close(&common_desc);
return -1;
}
if(test_actions(inot_dir, file_path, link_path) < 0) {
common_close(&common_desc);
return -1;
}
if (wait) {
do_wait();
}
len = read_set(common_desc.inot, event_set);
common_close(&common_desc);
#ifdef NODEL
if (! (fDelete(file_path) == 0 &&
fDelete(link_path) == 0 &&
fRemDir(inot_dir) == 0))
return -1;
#endif
return len;
}
int check(int len, char *event_set, int exp_len, char *etalon_event_set) {
if ((exp_len < 0) || (len < 0)){
fail("Error in preparing event sets.");
return -1;
}
if (len < exp_len) {
fail("Events are lost. Read: %d, Expected: %d", len, exp_len);
test_msg("expected events\n");
dump_events(etalon_event_set, exp_len);
test_msg("real events\n");
dump_events(event_set, len);
return -1;
}
if (errors(exp_len, len, etalon_event_set, event_set) == 0) {
pass();
return 0;
}
return -1;
}
int main(int argc, char ** argv)
{
int exp_len=-1, len=-1;
char etalon_event_set[EVENT_BUF_LEN];
char event_set[EVENT_BUF_LEN];
test_init(argc, argv);
exp_len = get_event_set(etalon_event_set, 0);
len = get_event_set(event_set, 1);
if (check(len, event_set, exp_len, etalon_event_set)) {
return 1;
}
return 0;
}
#else
int main(int argc, char ** argv)
{
test_init(argc, argv);
skip("Inotify not supported.");
return 0;
}
#endif //__NR_inotify_init
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <signal.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Inotify on symlink should be checked";
#ifndef NODEL
char filename[] = "file";
char linkname[] = "file.lnk";
const char *inot_dir = "./inotify";
#else
char filename[] = "file.nodel";
char linkname[] = "file.nodel.lnk";
const char *inot_dir = "./inotify.nodel";
#endif
#ifdef __NR_inotify_init
#include <sys/inotify.h>
#ifndef IN_DONT_FOLLOW
/* Missed in SLES 10 header */
#define IN_DONT_FOLLOW 0x02000000
#endif
#define EVENT_MAX 1024
/* size of the event structure, not counting name */
#define EVENT_SIZE (sizeof (struct inotify_event))
/* reasonable guess as to size of 1024 events */
#define EVENT_BUF_LEN (EVENT_MAX * (EVENT_SIZE + 16))
#define BUF_SIZE 256
#define min_value(a,b) (a<b) ? a : b
#define handle_event(MASK) (MASK == IN_ACCESS) ? "IN_ACCESS" : \
(MASK == IN_MODIFY) ? "IN_MODIFY" : \
(MASK == IN_ATTRIB) ? "IN_ATTRIB" : \
(MASK == IN_CLOSE) ? "IN_CLOSE" : \
(MASK == IN_CLOSE_WRITE) ? "IN_CLOSE_WRITE" : \
(MASK == IN_CLOSE_NOWRITE) ? "IN_CLOSE_NOWRITE" : \
(MASK == IN_OPEN) ? "IN_OPEN" : \
(MASK == IN_MOVED_FROM) ? "IN_MOVED_FROM" : \
(MASK == IN_MOVED_TO) ? "IN_MOVED_TO" : \
(MASK == IN_DELETE) ? "IN_DELETE" : \
(MASK == IN_CREATE) ? "IN_CREATE" : \
(MASK == IN_DELETE_SELF) ? "IN_DELETE_SELF" : \
(MASK == IN_MOVE_SELF) ? "IN_MOVE_SELF" : \
(MASK == IN_UNMOUNT) ? "IN_UNMOUNT" : \
(MASK == IN_Q_OVERFLOW) ? "IN_Q_OVERFLOW" : \
(MASK == IN_IGNORED) ? "IN_IGNORED" : \
"UNKNOWN"
#include <unistd.h>
#include <fcntl.h>
typedef struct {
int inot;
uint32_t file;
uint32_t link;
uint32_t dir;
} desc;
void do_wait() {
test_daemon();
test_waitsig();
}
int createFiles(char *path, char *target, char *link) {
int fd;
fd = open(path,O_CREAT, 0644);
if (fd < 0) {
err("can't open %s: %m", path);
return -1;
}
close(fd);
if (symlink(target, link) < 0) {
err("can't symlink %s to %s: %m", path, link);
return -1;
}
return 0;
}
int addWatcher(int fd, const char *path) {
int wd;
wd = inotify_add_watch(fd, path, IN_ALL_EVENTS | IN_DONT_FOLLOW);
if (wd < 0) {
err("inotify_add_watch(%d, %s, IN_ALL_EVENTS) Failed, %s",
fd, path, strerror(errno));
return -1;
}
return wd;
}
int fChmod(char *path) {
if (chmod(path, 0755) < 0) {
err("chmod(%s, 0755) Failed, %s",
path, strerror(errno));
return -1;
}
return 0;
}
int fWriteClose(char *path) {
int fd = open(path, O_RDWR | O_CREAT, 0700);
if (fd == -1) {
err("open(%s, O_RDWR|O_CREAT,0700) Failed, %s",
path, strerror(errno));
return -1;
}
if (write(fd, "string", 7) == -1) {
err("write(%d, %s, 1) Failed, %s", fd, path, strerror(errno));
return -1;
}
if (close(fd) == -1) {
err("close(%s) Failed, %s", path, strerror(errno));
return -1;
}
return 0;
}
int fNoWriteClose(char *path) {
char buf[BUF_SIZE];
int fd = open(path, O_RDONLY);
if ( fd < 0 ) {
err("open(%s, O_RDONLY) Failed, %s",
path, strerror(errno));
return -1;
}
if (read(fd, buf, BUF_SIZE) == -1) {
err("read error: %s", strerror(errno));
close(fd);
return -1;
}
if (close(fd) == -1) {
err("close(%s) Failed, %s", path, strerror(errno));
return -1;
}
return 0;
}
int fMove(char *from, char *to) {
if (rename(from, to) == -1) {
err("rename error (from: %s to: %s) : %s",
from, to, strerror(errno));
return -1;
}
return 0;
}
desc init_env(const char *dir, char *file_path, char *link_path) {
desc in_desc = {-1, -1, -1, -1};
if (mkdir(dir, 0777) < 0) {
err("error in creating directory: %s, %s",
dir, strerror(errno));
return in_desc;
}
in_desc.inot = inotify_init();
if (in_desc.inot < 0) {
err("inotify_init () Failed, %s", strerror(errno));
rmdir(dir);
return in_desc;
}
if (snprintf(file_path, BUF_SIZE, "%s/%s", dir, filename) >= BUF_SIZE) {
err("filename %s is too long", filename);
rmdir(dir);
return in_desc;
}
if (snprintf(link_path, BUF_SIZE, "%s/%s", dir, linkname) >= BUF_SIZE) {
err("filename %s is too long", linkname);
rmdir(dir);
return in_desc;
}
in_desc.dir = addWatcher(in_desc.inot, dir);
if (createFiles(file_path, filename, link_path)) {
return in_desc;
}
in_desc.link = addWatcher(in_desc.inot, link_path);
in_desc.file = addWatcher(in_desc.inot, file_path);
return in_desc;
}
int fDelete(char *path) {
if (unlink(path) != 0) {
err("unlink: (%s)", strerror(errno));
return -1;
}
return 0;
}
int fRemDir(const char *target) {
if(rmdir(target)) {
err("rmdir: (%s)", strerror(errno));
return -1;
}
return 0;
}
int test_actions(const char *dir, char *file_path, char *link_path) {
if (
fChmod(link_path) == 0 &&
fWriteClose(link_path) == 0 &&
fNoWriteClose(link_path) == 0 &&
fMove(file_path, filename) == 0 &&
fMove(filename, file_path) == 0
#ifndef NODEL
&& fDelete(file_path) == 0 &&
fDelete(link_path) == 0 &&
fRemDir(dir) == 0
#endif
)
{
return 0;
}
return -1;
}
void dump_events(char *buf, int len) {
int marker = 0;
struct inotify_event *event;
while (marker < len) {
event = (struct inotify_event *) &buf[marker];
test_msg("\t%s (%x mask, %d len", handle_event(event->mask), event->mask, event->len);
if (event->len)
test_msg(", '%s' name", event->name);
test_msg(")\n");
marker += EVENT_SIZE + event->len;
}
}
int harmless(int mask)
{
switch (mask) {
case IN_CLOSE_NOWRITE:
case IN_ATTRIB:
return 1;
}
return 0;
}
int errors(int exp_len, int len, char *etalon_buf, char *buf) {
int marker=0;
int error=0;
while (marker < len){
struct inotify_event *event;
struct inotify_event *exp_event;
event = (struct inotify_event *) &buf[marker];
/* It's OK if some additional events are recevived */
if (marker < exp_len)
exp_event = (struct inotify_event *) &etalon_buf[marker];
else {
if (!harmless(event->mask)) {
fail("got unexpected event %s\n",
handle_event(event->mask));
error++;
}
goto next_event;
}
if (event->mask != exp_event->mask) {
fail("Handeled %s (%x mask), expected %s (%x mask)",
handle_event(event->mask), event->mask,
handle_event(exp_event->mask),
exp_event->mask);
error++;
}
if (event->len != exp_event->len) {
fail("Incorrect length of field name.");
error++;
break;
}
else if (event->len && strncmp(event->name, exp_event->name, event->len)) {
fail("Handeled file name %s, expected %s",
event->name,
exp_event->name);
error++;
}
next_event:
marker += EVENT_SIZE + event->len;
}
return error;
}
int read_set(int inot_fd, char *event_set) {
int len;
if ((len = read(inot_fd, event_set, EVENT_BUF_LEN)) < 0) {
err("read(%d, buf, %d) Failed, errno=%d : %s",
inot_fd, EVENT_BUF_LEN, errno, strerror(errno));
return -1;
}
return len;
}
void common_close(desc *descr) {
if (descr->inot > 0) {
close(descr->inot);
descr->inot=-1;
descr->file=-1;
descr->dir=-1;
descr->link=-1;
}
}
int get_event_set(char *event_set, int wait) {
int len;
char link_path[BUF_SIZE];
char file_path[BUF_SIZE];
desc common_desc;
common_desc = init_env(inot_dir, file_path, link_path);
if ((common_desc.inot < 0) || (common_desc.file < 0) || \
(common_desc.dir < 0) || (common_desc.link < 0)) {
common_close(&common_desc);
return -1;
}
if(test_actions(inot_dir, file_path, link_path) < 0) {
common_close(&common_desc);
return -1;
}
if (wait) {
do_wait();
}
len = read_set(common_desc.inot, event_set);
common_close(&common_desc);
#ifdef NODEL
if (! (fDelete(file_path) == 0 &&
fDelete(link_path) == 0 &&
fRemDir(inot_dir) == 0))
return -1;
#endif
return len;
}
int check(int len, char *event_set, int exp_len, char *etalon_event_set) {
if ((exp_len < 0) || (len < 0)){
fail("Error in preparing event sets.");
return -1;
}
if (len < exp_len) {
fail("Events are lost. Read: %d, Expected: %d", len, exp_len);
test_msg("expected events\n");
dump_events(etalon_event_set, exp_len);
test_msg("real events\n");
dump_events(event_set, len);
return -1;
}
if (errors(exp_len, len, etalon_event_set, event_set) == 0) {
pass();
return 0;
}
return -1;
}
int main(int argc, char ** argv)
{
int exp_len=-1, len=-1;
char etalon_event_set[EVENT_BUF_LEN];
char event_set[EVENT_BUF_LEN];
test_init(argc, argv);
exp_len = get_event_set(etalon_event_set, 0);
len = get_event_set(event_set, 1);
if (check(len, event_set, exp_len, etalon_event_set)) {
return 1;
}
return 0;
}
#else
int main(int argc, char ** argv)
{
test_init(argc, argv);
skip("Inotify not supported.");
return 0;
}
#endif //__NR_inotify_init
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <pty.h>
#include "zdtmtst.h"
const char *test_doc = "Check that job control migrates correctly";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
#define JOBS_DEF 8
#define JOBS_MAX 64
unsigned int num_jobs = JOBS_DEF;
TEST_OPTION(num_jobs, uint, "# \"jobs\" in a \"shell\" "
"(default " __stringify(JOBS_DEF)
", max " __stringify(JOBS_MAX) ")", 0);
#define PROCS_DEF 4
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes in a \"job\" "
"(default " __stringify(PROCS_DEF) ")", 0);
static const char wr_string[] = "All you need is love!\n";
static const char rd_string[] = "We all live in a yellow submarine\n";
static const char susp_char = '\032'; /* ^Z */
static volatile sig_atomic_t signo = 0;
static void record_sig(int sig)
{
signo = sig;
}
static void record_and_raise_sig(int sig)
{
signo = sig;
signal(sig, SIG_DFL);
raise(sig);
}
static int wait4sig(int sig)
{
sigset_t mask, oldmask;
sigemptyset(&mask);
sigaddset(&mask, sig);
sigaddset(&mask, SIGCHLD); /* to see our children die */
sigprocmask(SIG_BLOCK, &mask, &oldmask);
while (!signo)
sigsuspend (&oldmask);
sigprocmask (SIG_UNBLOCK, &mask, NULL);
return signo != sig;
}
static int is_fg(void)
{
pid_t pgid = getpgrp();
pid_t tcpgid = tcgetpgrp(1);
return (pgid != -1) && (pgid == tcpgid);
}
static int reader(int sig)
{
char str[sizeof(rd_string) + 1];
return read(0, str, sizeof(str)) < 0 ||
strcmp(str, rd_string);
}
static int post_reader(int fd)
{
if (write(fd, rd_string, sizeof(rd_string) - 1) < 0) {
fail("write failed: %m");
return -1;
}
return 0;
}
static int writer(int sig)
{
return write(1, wr_string, sizeof(wr_string) - 1) < 0;
}
static int post_writer(int fd)
{
char str[sizeof(wr_string) + 1];
if (read(0, str, sizeof(str)) < 0) {
fail("read failed: %m");
return -1;
}
/*
if (strcmp(str, wr_string)) {
fail("read string mismatch");
return -1;
}
*/
return 0;
}
static struct job_type {
int sig;
int (*action)(int sig);
int (*post)(int fd);
} job_types[] = {
{ SIGTTOU, writer, post_writer },
{ SIGTTIN, reader, post_reader },
{ SIGCONT, wait4sig, NULL },
};
static int process(int (*action)(int), int sig)
{
int ret;
if (is_fg()) /* we must be in background on entry */
return 1;
if (signal(sig, record_and_raise_sig) == SIG_ERR)
return 2;
kill(getppid(), SIGUSR2); /* tell the parent we're ready */
ret = action(sig); /* will be busy doing nothing for the duration of migration */
if (ret)
return 3;
if (!is_fg()) /* we must be in foreground now */
return 4;
ret = signo != sig; /* have we got the desired signal? */
test_waitsig();
return ret;
}
static int job(int (*action)(int), int sig)
{
int i;
if (setpgrp() < 0)
return 1;
for (i = num_procs; i; i--) {
pid_t pid = fork();
if (pid < 0)
kill(0, SIGKILL); /* kill the whole job */
if (pid == 0)
/* the last is worker, others are sleepers */
exit(process(i == 1 ? action : wait4sig, sig));
/* wait for the child to grow up before going to next one
* ignore return code as the child may get stopped and SIGCHILD
* us */
wait4sig(SIGUSR2);
signo = 0; /* rearm sighandler */
}
kill(getppid(), SIGUSR2); /* tell the parent we're ready */
/* we (or our children) will get suspended somehow here, so the rest
* will hopefully happen after migration */
for (i = num_procs; i; i--) {
int ret;
wait(&ret);
if (!WIFEXITED(ret) || WEXITSTATUS(ret))
kill(0, SIGKILL);
}
return 0;
}
static int make_pty_pair(int *fdmaster, int *fdslave)
{
struct termios tio;
if (openpty(fdmaster, fdslave, NULL, &tio, NULL) < 0)
return -1;
if (ioctl(*fdslave, TIOCSCTTY, NULL) < 0)
return -1;
tio.c_lflag |= (ICANON | ISIG | TOSTOP);
if (tcsetattr(*fdslave, TCSANOW, &tio) < 0)
return -1;
return 0;
}
int start_jobs(pid_t *jobs, int njobs, int fdmaster, int fdslave)
{
int i;
/* the children will signal readiness via SIGUSR2 or get stopped (or
* exit :) and signal that via SIGCHLD */
if (signal(SIGUSR2, record_sig) == SIG_ERR ||
signal(SIGCHLD, record_sig) == SIG_ERR) {
err("can't install signal handler: %m");
return -1;
}
for (i = 0; i < njobs; i++) {
int jtno = i % (sizeof(job_types) / sizeof(job_types[0]));
jobs[i] = fork();
if (jobs[i] < 0) { /* we're busted - bail out */
err("fork failed: %m");
goto killout;
}
if (jobs[i] == 0) {
close(fdmaster);
dup2(fdslave, 0);
dup2(fdslave, 1);
dup2(fdslave, 2);
close(fdslave);
exit(job(job_types[jtno].action, job_types[jtno].sig));
}
/* wait for the child to grow up before proceeding */
wait4sig(SIGUSR2);
signo = 0; /* rearm sighandler */
}
return 0;
killout:
for (; i >= 0; i--)
kill(-jobs[i], SIGKILL);
return -1;
}
int finish_jobs(pid_t *jobs, int njobs, int fdmaster, int fdslave)
{
int i;
for (i = num_jobs; i--; ) {
int ret;
int jtno = i % (sizeof(job_types) / sizeof(job_types[0]));
if (tcsetpgrp(fdslave, jobs[i]) < 0) {
fail("can't bring a job into foreground: %m");
goto killout;
}
kill(-jobs[i], SIGCONT);
if (job_types[jtno].post && job_types[jtno].post(fdmaster))
goto killout;
kill(-jobs[i], SIGTERM);
waitpid(jobs[i], &ret, 0);
if (!WIFEXITED(ret) || WEXITSTATUS(ret)) {
fail("job didn't exit cleanly: %d", ret);
goto killout;
}
}
return 0;
killout:
for (; i >= 0; i--)
kill(-jobs[i], SIGKILL);
return -1;
}
int main(int argc, char ** argv)
{
int fdmaster, fdslave;
pid_t jobs[JOBS_MAX] = {};
test_init(argc, argv);
if (num_jobs > JOBS_MAX) {
err("%d jobs is too many", num_jobs);
exit(1);
}
if (make_pty_pair(&fdmaster, &fdslave) < 0) {
err("can't make pty pair: %m");
exit(1);
}
sleep(30);
if (start_jobs(jobs, num_jobs, fdmaster, fdslave)) {
err("failed to start jobs");
exit(1);
}
test_daemon();
test_waitsig();
if (finish_jobs(jobs, num_jobs, fdmaster, fdslave))
fail("failed to finish jobs");
else
pass();
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Migrate two hardlinked, open, and unlinked files";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char ** argv)
{
int fd, fd2 = 0;
struct stat stat, stat2;
char filename2[256];
test_init(argc, argv);
if (snprintf(filename2, sizeof(filename2), "%s.lnk", filename) >=
sizeof(filename2)) {
err("filename %s is too long", filename);
exit(1);
}
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
if (fd < 0) {
err("can't open %s: %m", filename);
exit(1);
}
if (link(filename, filename2) < 0) {
err("can't link %s to %s: %m", filename, filename2);
goto unlink;
}
fd2 = open(filename2, O_RDONLY);
if (fd < 0) {
err("can't open %s: %m", filename2);
goto unlink;
}
unlink(filename2);
unlink(filename);
test_daemon();
test_waitsig();
if (fstat(fd, &stat) < 0 || fstat(fd2, &stat2) < 0) {
fail("fstat failed: %m");
goto out;
}
if (stat.st_ino != stat2.st_ino ||
stat.st_dev != stat2.st_dev) {
fail("files are different: st_ino %d != %d or st_dev %d != %d",
stat.st_ino, stat2.st_ino, stat.st_dev, stat2.st_dev);
}
pass();
out:
close(fd);
close(fd2);
return 0;
unlink:
close(fd);
close(fd2);
unlink(filename2);
unlink(filename);
return 1;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "zdtmtst.h"
const char *test_doc = "Create all sorts of maps and compare /proc/pid/maps\n"
"before and after migration\n";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
const static int map_prots[] = {
PROT_NONE,
PROT_READ,
PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PROT_EXEC,
};
#define NUM_MPROTS sizeof(map_prots) / sizeof(int)
#define RW_PROT(x) ((x) & (PROT_READ | PROT_WRITE))
#define X_PROT(x) ((x) & PROT_EXEC)
int check_prot(int src_prot, int dst_prot)
{
if (RW_PROT(src_prot) != RW_PROT(dst_prot))
return 0;
/* If exec bit will be enabled may depend on NX capablity of CPUs of
* source and destination nodes. In any case, migrated mapping should
* not have less permissions than newly created one
**
* A is a subset of B iff (A & B) == A
*/
return (X_PROT(dst_prot) & X_PROT(src_prot)) == X_PROT(dst_prot);
}
const static int map_flags[] = {
MAP_PRIVATE,
MAP_SHARED,
MAP_PRIVATE | MAP_ANONYMOUS,
MAP_SHARED | MAP_ANONYMOUS
};
#define NUM_MFLAGS sizeof(map_flags) / sizeof(int)
#define NUM_MAPS NUM_MPROTS * NUM_MFLAGS
#define ONE_MAP_SIZE 0x2000
struct map
{
int prot;
int prot_real;
int flag;
char filename[256];
int fd;
void *ptr;
};
static void init_map(struct map *map, int prot_no, int flag_no)
{
map->fd = -1;
map->prot = map_prots[prot_no];
map->flag = map_flags[flag_no];
}
static int make_map(struct map *map)
{
uint32_t crc;
uint8_t buf[ONE_MAP_SIZE];
static int i = 0;
if (!(map->flag & MAP_ANONYMOUS)) {
/* need file */
if (snprintf(map->filename, sizeof(map->filename),
"%s-%02d", filename, i++) >= sizeof(map->filename)) {
err("filename %s is too long\n", filename);
return -1;
}
map->fd = open(map->filename, O_RDWR | O_CREAT, 0600);
if (map->fd < 0) {
err("can't open %s: %m\n", map->filename);
return -1;
}
crc = ~0;
datagen(buf, sizeof(buf), &crc);
if (write(map->fd, buf, sizeof(buf)) != sizeof(buf)) {
err("failed to write %s: %m\n", map->filename);
return -1;
}
}
map->ptr = mmap(NULL, ONE_MAP_SIZE, map->prot, map->flag, map->fd, 0);
if (map->ptr == MAP_FAILED) {
err("can't create mapping: %m\n");
return -1;
}
if ((map->flag & MAP_ANONYMOUS) && (map->prot & PROT_WRITE)) {
/* can't fill it with data otherwise */
crc = ~0;
datagen(map->ptr, ONE_MAP_SIZE, &crc);
}
return 0;
}
static sigjmp_buf segv_ret; /* we need sig*jmp stuff, otherwise SIGSEGV will reset our handler */
static void segfault(int signo)
{
siglongjmp(segv_ret, 1);
}
/*
* after test func should be placed check map, because size of test_func
* is calculated as (check_map-test_func)
*/
int test_func()
{
return 1;
}
static int check_map(struct map *map)
{
int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
if (signal(SIGSEGV, segfault) == SIG_ERR)
{
fail("setting SIGSEGV handler failed: %m\n");
return -1;
}
if (!sigsetjmp(segv_ret, 1))
{
uint32_t crc = ~0;
if (datachk(map->ptr, ONE_MAP_SIZE, &crc)) /* perform read access */
if (!(map->flag & MAP_ANONYMOUS) ||
(map->prot & PROT_WRITE)) { /* anon maps could only be filled when r/w */
fail("CRC mismatch\n");
return -1;
}
/* prot |= PROT_READ// need barrier before this line,
because compiler change order commands.
I finded one method: look at next lines*/
} else
prot &= PROT_WRITE | !PROT_READ | PROT_EXEC;
if (signal(SIGSEGV, segfault) == SIG_ERR)
{
fail("setting SIGSEGV handler failed: %m\n");
return -1;
}
if (!sigsetjmp(segv_ret, 1))
{
* (int *) (map->ptr) = 1234; /* perform write access */
} else
prot &= !PROT_WRITE | PROT_READ | PROT_EXEC;
if (signal(SIGSEGV, segfault) == SIG_ERR)
{
fail("restoring SIGSEGV handler failed: %m\n");
return -1;
}
if (!sigsetjmp(segv_ret, 1))
{
if (map->prot & PROT_WRITE) {
memcpy(map->ptr,test_func, getpagesize());
} else {
if (!(map->flag & MAP_ANONYMOUS)) {
lseek(map->fd,0,SEEK_SET);
if (write(map->fd,test_func,check_map - test_func)<check_map - test_func) {
err("failed to write %s: %m\n", map->filename);
return -1;
}
}
}
if (!(map->flag & MAP_ANONYMOUS) || map->prot & PROT_WRITE)
/* Function body has been copied into the mapping */
((int (*)())map->ptr)(); /* perform exec access */
else
/* No way to copy function body into mapping,
* clear exec bit from effective protection
*/
prot &= PROT_WRITE | PROT_READ | !PROT_EXEC;
} else
prot &= PROT_WRITE | PROT_READ | !PROT_EXEC;
if (signal(SIGSEGV, SIG_DFL) == SIG_ERR)
{
fail("restoring SIGSEGV handler failed: %m\n");
return -1;
}
return prot;
}
static void destroy_map(struct map *map)
{
munmap(map->ptr, ONE_MAP_SIZE);
if (map->fd >= 0)
{
close(map->fd);
unlink(map->filename);
}
}
#define MAPS_LEN 0x10000
int main(int argc, char ** argv)
{
struct map maps[NUM_MAPS] = {}, maps_compare[NUM_MAPS] = {};
int i, j, k;
test_init(argc, argv);
k = 0;
for (i = 0; i < NUM_MPROTS; i++)
for (j = 0; j < NUM_MFLAGS; j++)
init_map(maps + k++, i, j);
for (i = 0; i < NUM_MAPS; i++)
if (make_map(maps + i))
goto err;
test_daemon();
test_waitsig();
for (i = 0; i < NUM_MAPS; i++)
if ((maps[i].prot_real=check_map(maps + i))<0)
goto err;
k=0;
for (i = 0; i < NUM_MPROTS; i++)
for (j = 0; j < NUM_MFLAGS; j++)
init_map(maps_compare + k++, i, j);
for (i = 0; i < NUM_MAPS; i++)
if (make_map(maps_compare+ i))
goto err;
for (i = 0; i < NUM_MAPS; i++)
if ((maps_compare[i].prot_real=check_map(maps_compare + i))<0)
goto err;
for (i = 0; i< NUM_MAPS; i++)
if (!check_prot(maps[i].prot_real, maps_compare[i].prot_real)){
fail("protection on %i (flag=%d prot=%d) maps has changed (prot=%d(expected %d))",
i, maps[i].flag, maps[i].prot, maps[i].prot_real, maps_compare[i].prot_real);
goto err;
}
pass();
for (i = 0; i < NUM_MAPS; i++) {
destroy_map(maps + i);
destroy_map(maps_compare + i);
}
return 0;
err:
return 1;
}
#include <string.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Start a calculation, leaving MMX in a certain state,\n"
"before migration, continue after";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#if defined(__i386__) || defined(__x86_64__)
void start(uint8_t *bytes, uint16_t *words)
{
__asm__ volatile (
"movq %0, %%mm0\n"
"movq %1, %%mm1\n"
"movq %2, %%mm2\n"
"movq %3, %%mm3\n"
"paddb %%mm0, %%mm1\n"
"psubw %%mm2, %%mm3\n"
:
: "m" (bytes[0]), "m" (bytes[8]),
"m" (words[0]), "m" (words[8])
);
}
void finish(uint8_t *bytes, uint16_t *words)
{
__asm__ volatile (
"movq %%mm1, %0\n"
"movq %%mm3, %1\n"
: "=m" (bytes[0]), "=m" (words[0])
);
}
static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
{
__asm__("cpuid"
: "=a" (*eax),
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (op), "c"(0));
}
int chk_proc_mmx(void)
{
unsigned int eax, ebx, ecx, edx;
cpuid(1, &eax, &ebx, &ecx, &edx);
return edx & (1 << 23);
}
#endif
int main(int argc, char **argv)
{
uint8_t bytes[16];
uint16_t words[8];
uint32_t rnd[8];
int i;
uint8_t resbytes1[8], resbytes2[8];
uint16_t reswords1[4], reswords2[4];
test_init(argc, argv);
#if defined(__i386__) || defined(__x86_64__)
if (!chk_proc_mmx()) {
skip("MMX not supported");
return 1;
}
for (i = 0; i < (sizeof(bytes) + sizeof(words)) / 4; i++)
rnd[i] = mrand48();
memcpy((uint8_t *) bytes, (uint8_t *) rnd, sizeof(bytes));
memcpy((uint8_t *) words, (uint8_t *) rnd + sizeof(bytes), sizeof(words));
start(bytes, words);
finish(resbytes1, reswords1);
start(bytes, words);
test_daemon();
test_waitsig();
finish(resbytes2, reswords2);
if (memcmp((uint8_t *) resbytes1, (uint8_t *) resbytes2, sizeof(resbytes1)))
fail("byte op mismatch\n");
else if (memcmp((uint8_t *) reswords1, (uint8_t *) reswords2, sizeof(reswords2)))
fail("word op mismatch\n");
else
pass();
#else
skip("Unsupported arch");
#endif
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <setjmp.h>
#include <limits.h>
#include "zdtmtst.h"
const char *test_doc = "Check that memory protection migrates correctly\n";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
const static int prots[] = {
PROT_NONE,
PROT_READ,
/* PROT_WRITE, */ /* doesn't work w/o READ */
PROT_READ | PROT_WRITE,
PROT_READ | PROT_WRITE | PROT_EXEC,
};
#define NUM_MPROTS sizeof(prots) / sizeof(int)
static sigjmp_buf segv_ret; /* we need sig*jmp stuff, otherwise SIGSEGV will reset our handler */
static void segfault(int signo)
{
siglongjmp(segv_ret, 1);
}
static int check_prot(char *ptr, int prot)
{
if (signal(SIGSEGV, segfault) == SIG_ERR) {
fail("setting SIGSEGV handler failed: %m\n");
return -1;
}
if (!sigsetjmp(segv_ret, 1)) {
if (ptr[10] != 0) {
fail("read value doesn't match what I wrote");
return -1;
}
if (!(prot & PROT_READ)) {
fail("PROT_READ bypassed\n");
return -1;
}
}
else /* we come here on return from SIGSEGV handler */
if (prot & PROT_READ) {
fail("PROT_READ rejected\n");
return -1;
}
if (!sigsetjmp(segv_ret, 1)) {
ptr[20] = 67;
if (!(prot & PROT_WRITE)) {
fail("PROT_WRITE bypassed\n");
return -1;
}
}
else /* we come here on return from SIGSEGV handler */
if (prot & PROT_WRITE) {
fail("PROT_WRITE rejected\n");
return -1;
}
if (signal(SIGSEGV, SIG_DFL) == SIG_ERR) {
fail("restoring SIGSEGV handler failed: %m\n");
return -1;
}
return 0;
}
int main(int argc, char ** argv)
{
char *ptr, *ptr_aligned;
int pagesize;
int i;
test_init(argc, argv);
pagesize = sysconf(_SC_PAGESIZE);
if (pagesize < 0) {
err("can't get PAGE_SIZE: %m");
exit(1);
}
ptr = calloc(pagesize, NUM_MPROTS + 1);
if (!ptr) {
err("calloc failed: %m");
return -1;
}
ptr_aligned = (char *)(((unsigned long) ptr + pagesize - 1) &
~(pagesize - 1));
for (i = 0; i < NUM_MPROTS; i++)
if (mprotect(ptr_aligned + pagesize * i,
pagesize / 2, prots[i]) < 0) {
err("mprotect failed: %m");
free(ptr);
exit(1);
}
test_daemon();
test_waitsig();
for (i = 0; i < NUM_MPROTS; i++)
if (check_prot(ptr_aligned + pagesize * i, prots[i]))
goto out;
pass();
out:
free(ptr);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <errno.h>
#include "zdtmtst.h"
const char *test_doc="Tests sysv5 msg queues supporting by checkpointing";
const char *test_author="Pavel Emelianov <xemul@parallels.com>";
struct msg1 {
long mtype;
char mtext[20];
};
#define TEST_STRING "Test sysv5 msg"
int main(int argc, char **argv)
{
key_t key;
int msg, pid;
struct msg1 msgbuf;
int chret;
test_init(argc, argv);
key = ftok(argv[0], 822155650);
if (key == -1) {
err("Can't make key");
exit(1);
}
pid = test_fork();
if (pid < 0) {
err("Can't fork");
exit(1);
}
msg = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msg == -1) {
msg = msgget(key, 0666);
if (msg == -1) {
err("Can't get queue");
if (pid) {
kill(pid, SIGKILL);
wait(NULL);
}
exit(1);
}
}
if (pid == 0) {
if (msgrcv(msg, &msgbuf, sizeof(TEST_STRING), 1, 0) == -1) {
chret = errno;
fail("msgrcv failed %d(%m)", errno);
return chret;
}
if (strncmp(TEST_STRING, msgbuf.mtext, sizeof(TEST_STRING))) {
fail("The source and received strings aren't equal");
return 1;
}
test_msg("Recived %s\n", msgbuf.mtext);
pass();
goto out;
} else {
test_daemon();
test_waitsig();
msgbuf.mtype = 1;
memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING));
if (msgsnd(msg, &msgbuf, sizeof(TEST_STRING), 0) != 0) {
fail("msgsnd failed %d(%m)", errno);
kill(pid, SIGKILL);
wait(NULL);
return 1;
};
wait(&chret);
chret = WEXITSTATUS(chret);
if (chret) {
fail("child exited with non-zero code %d (%s)\n",
chret, strerror(chret));
return 1;
}
pass();
}
msgctl(msg, IPC_RMID, 0);
out:
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "zdtmtst.h"
const char *test_doc = "file mmaped for write and being written should change mtime\n"
"and be migrated with correct new data";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
#define FILE_SIZE (16 * 1024)
int main(int argc, char **argv)
{
int fd;
char buf[FILE_SIZE];
size_t count;
int i;
char *ptr;
struct stat fst;
time_t mtime_old, mtime_new;
time_t ctime_old, ctime_new;
test_init(argc, argv);
fd = open(filename, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
/* initialization */
count = sizeof(buf);
memset(buf, 1, count);
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
err("failed to write %s: %m\n", filename);
exit(1);
}
if (fstat(fd, &fst) < 0) {
err("can't get %s file info: %m\n", filename);
goto failed;
}
ptr = (char *)mmap(NULL, count, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
err("mmap() Failed, errno=%d : %s", errno, strerror(errno));
goto failed;
}
mtime_old = fst.st_mtime;
ctime_old = fst.st_ctime;
sleep(2);
for (i = 0; i < count; i++)
ptr[i]++;
if (munmap(ptr, count)) {
err("munmap Failed, errno=%d : %s", errno, strerror(errno));
goto failed;
}
if (fstat(fd, &fst) < 0) {
err("can't get %s file info: %m\n", filename);
goto failed;
}
mtime_new = fst.st_mtime;
/* time of last modification */
if (mtime_new <= mtime_old) {
fail("mtime %d wasn't updated on mmapped %s file",
mtime_new, filename);
goto failed;
}
ctime_new = fst.st_ctime;
/* time of last status change */
if (ctime_new <= ctime_old) {
fail("time of last status change of %s file wasn't changed\n",
filename);
goto failed;
}
test_daemon();
test_waitsig();
if (fstat(fd, &fst) < 0) {
err("can't get %s file info: %m\n", filename);
goto failed;
}
/* time of last modification */
if (fst.st_mtime != mtime_new) {
fail("After migration, mtime changed to %d",
fst.st_mtime);
goto failed;
}
pass();
unlink(filename);
close(fd);
return 0;
failed:
return 1;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a device special file "
"open in a directory which has been mounted over by "
"another filesystem";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
int main(int argc, char **argv)
{
int fd;
char path[256];
struct stat st;
/* /dev/null params - sure to exist in a VPS */
mode_t mode = S_IFCHR | 0700;
dev_t dev = makedev(1, 3);
test_init(argc, argv);
if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
err("directory name \"%s\"is too long", dirname);
exit(1);
}
if (mkdir(dirname, 0700)) {
err("can't make directory %s: %m\n", dirname);
exit(1);
}
if (mknod(path, mode, dev)) {
err("can't make device file \"%s\": %m\n", path);
exit(1);
}
fd = open(path, O_RDWR);
if (fd < 0) {
err("can't open %s: %m\n", path);
goto rmdir;
}
if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
err("can't mount tmpfs over %s: %m", dirname);
goto cleanup;
}
test_daemon();
test_waitsig();
if (umount(dirname) < 0) {
fail("can't umount %s: %m", dirname);
goto cleanup;
}
if (close(fd) < 0) {
fail("can't close %s: %m", path);
goto unlink;
}
if (stat(path, &st) < 0) {
fail("can't stat %s: %m", path);
goto unlink;
}
if (st.st_mode != mode || st.st_rdev != dev) {
fail("%s is no longer the device file we had");
goto unlink;
}
if (unlink(path) < 0) {
fail("can't unlink %s: %m", path);
goto rmdir;
}
pass();
goto rmdir;
cleanup:
close(fd);
unlink:
unlink(path);
rmdir:
rmdir(dirname);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a named pipe "
"open in a directory which has been mounted over by "
"another filesystem";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
int main(int argc, char **argv)
{
int fd;
char path[256];
struct stat st;
mode_t mode = S_IFIFO | 0700;
test_init(argc, argv);
if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
err("directory name \"%s\"is too long", dirname);
exit(1);
}
if (mkdir(dirname, 0700)) {
err("can't make directory %s: %m\n", dirname);
exit(1);
}
if (mknod(path, mode, 0)) {
err("can't make fifo \"%s\": %m\n", path);
exit(1);
}
fd = open(path, O_RDWR);
if (fd < 0) {
err("can't open %s: %m\n", path);
goto rmdir;
}
if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
err("can't mount tmpfs over %s: %m", dirname);
goto cleanup;
}
test_daemon();
test_waitsig();
if (umount(dirname) < 0) {
fail("can't umount %s: %m", dirname);
goto cleanup;
}
if (close(fd) < 0) {
fail("can't close %s: %m", path);
goto unlink;
}
if (stat(path, &st) < 0) {
fail("can't stat %s: %m", path);
goto unlink;
}
if (st.st_mode != mode) {
fail("%s is no longer the fifo we had");
goto unlink;
}
if (unlink(path) < 0) {
fail("can't unlink %s: %m", path);
goto rmdir;
}
pass();
goto rmdir;
cleanup:
close(fd);
unlink:
unlink(path);
rmdir:
rmdir(dirname);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can't migrate with a file open in a "
"directory which has been mounted over by another "
"filesystem";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
int main(int argc, char **argv)
{
int fd;
char path[256];
test_init(argc, argv);
if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
err("directory name \"%s\"is too long", dirname);
exit(1);
}
if (mkdir(dirname, 0700)) {
err("can't make directory %s: %m\n", dirname);
exit(1);
}
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
err("can't open %s: %m\n", path);
goto rmdir;
}
if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
err("can't mount tmpfs over %s: %m", dirname);
goto cleanup;
}
test_daemon();
test_waitsig();
if (umount(dirname) < 0) {
fail("can't umount %s: %m", dirname);
goto cleanup;
}
if (close(fd) < 0) {
fail("can't close %s: %m", path);
goto unlink;
}
if (unlink(path) < 0) {
fail("can't unlink %s: %m", path);
goto rmdir;
}
pass();
goto rmdir;
cleanup:
close(fd);
unlink:
unlink(path);
rmdir:
rmdir(dirname);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Check that we can migrate with a unix socket "
"bound in a directory which has been mounted over by"
" another filesystem";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
static int fill_sock_name(struct sockaddr_un *name, const char *filename)
{
if (strlen(filename) >= sizeof(name->sun_path))
return -1;
name->sun_family = AF_LOCAL;
strcpy(name->sun_path, filename);
return 0;
}
static int setup_srv_sock(const char *filename)
{
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0) {
err("filename \"%s\" is too long", filename);
return -1;
}
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
err("can't create socket: %m");
return -1;
}
if (bind(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
err("can't bind to socket \"%s\": %m", filename);
goto err;
}
if (listen(sock, 1) < 0) {
err("can't listen on a socket \"%s\": %m\n", filename);
goto err;
}
return sock;
err:
close(sock);
return -1;
}
static int setup_clnt_sock(const char *filename)
{
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0)
return -1;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0)
return -1;
if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0)
goto err;
return sock;
err:
close(sock);
return -1;
}
int main(int argc, char ** argv)
{
int sock, acc_sock, ret;
char path[256];
pid_t pid;
uint32_t crc;
uint8_t buf[1000];
test_init(argc, argv);
if (snprintf(path, sizeof(path), "%s/foo", dirname) >= sizeof(path)) {
err("directory name \"%s\"is too long", dirname);
exit(1);
}
if (mkdir(dirname, 0700)) {
err("can't make directory %s: %m\n", dirname);
exit(1);
}
sock = setup_srv_sock(path);
if (sock < 0)
goto out;
pid = fork();
if (pid < 0) {
err("can't fork: %m\n");
goto out;
}
if (pid == 0) { /* child writes to the overmounted socket and returns */
close(sock);
sock = setup_clnt_sock(path);
if (sock < 0)
_exit(1);
test_waitsig();
crc = ~0;
datagen(buf, sizeof(buf), &crc);
if (write(sock, buf, sizeof(buf)) != sizeof(buf))
_exit(errno);
close(sock);
_exit(0);
}
acc_sock = accept(sock, NULL, NULL);
if (acc_sock < 0) {
err("can't accept() the connection on \"%s\": %m", path);
goto out_kill;
}
close(sock);
sock = acc_sock;
if (mount("rien", dirname, "tmpfs", 0, 0) < 0) {
err("can't mount tmpfs over %s: %m", dirname);
goto out_kill;
}
test_daemon();
test_waitsig();
if (kill(pid, SIGTERM)) {
fail("terminating the child failed: %m\n");
goto out;
}
if (wait(&ret) != pid) {
fail("wait() returned wrong pid %d: %m\n", pid);
goto out;
}
if (WIFEXITED(ret)) {
ret = WEXITSTATUS(ret);
if (ret) {
fail("child exited with nonzero code %d (%s)\n", ret,
strerror(ret));
goto out;
}
}
if (WIFSIGNALED(ret)) {
fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
goto out;
}
if (read(sock, buf, sizeof(buf)) != sizeof(buf)) {
fail("can't read %s: %m\n", path);
goto out;
}
crc = ~0;
if (datachk(buf, sizeof(buf), &crc)) {
fail("CRC mismatch\n");
goto out;
}
if (umount(dirname) < 0) {
fail("can't umount %s: %m", dirname);
goto out;
}
if (close(sock) < 0) {
fail("can't close %s: %m", path);
goto out;
}
if (unlink(path) < 0) {
fail("can't unlink %s: %m", path);
goto out;
}
pass();
out_kill:
kill(pid, SIGKILL);
out:
close(sock);
unlink(path);
rmdir(dirname);
return 0;
}
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include "zdtmtst.h"
const char *test_doc = "Check that p?pid and e?[ug]id didn't change";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
int main(int argc, char **argv)
{
pid_t pid, ppid;
uid_t uid, euid;
gid_t gid, egid;
test_init(argc, argv);
#define SET_XID(id) id = get##id()
SET_XID(pid);
ppid = 1; /* SET_XID(ppid); daemonization confuses it */
SET_XID(uid);
SET_XID(euid);
SET_XID(gid);
SET_XID(egid);
test_daemon();
test_waitsig();
#define CHECK(id) do { \
if (id != get##id()) { \
fail("%s != get%s()\n", #id, #id); \
goto out; \
} \
} while (0)
CHECK(pid);
CHECK(ppid);
CHECK(uid);
CHECK(euid);
CHECK(gid);
CHECK(egid);
pass();
out:
return 0;
}
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <signal.h>
#include "zdtmtst.h"
const char *test_doc = "Check ptrace, if the child process's stopped by signal";
const char *test_author = "Andrey Vagin <avagin@parallels.com>";
typedef void (*sighandler_t)(int);
int child_fd;
int child_exit = 0;
void sig_handler(int signo, siginfo_t *siginfo, void *data)
{
int pid, ret;
test_msg("receive signal sig=%d from pid=%d\n", signo, siginfo->si_pid);
pid = siginfo->si_pid;
ret = write(child_fd, &pid, sizeof(pid));
if (ret != sizeof(pid))
err("write");
child_exit = 1;
}
void sig_chld_handler(int signo)
{
test_msg("Receive signal %d\n", signo);
}
int child(fd)
{
int ret = 0;
struct sigaction act = {
.sa_sigaction = sig_handler,
.sa_flags = SA_SIGINFO,
}, old_act;
sigemptyset(&act.sa_mask);
child_fd = fd;
ret = sigaction(SIGUSR2, &act, &old_act);
if (ret < 0) {
err("signal failed\n");
return 1;
}
ret = ptrace(PTRACE_TRACEME, 0, 0, 0);
if (ret < 0) {
err("ptrace failed\n");
return 1;
}
ret = write(child_fd, &ret, sizeof(ret));
while (!child_exit)
ret = sleep(1);
close(child_fd);
return 0;
}
int main(int argc, char ** argv)
{
int ret, status = 0;
pid_t pid, spid, cpid;
sighandler_t sh;
int signal_pipe[2];
int child_pipe[2];
test_init(argc, argv);
ret = pipe(child_pipe);
if (ret < 0) {
err("pipe failed");
return 1;
}
cpid = test_fork();
if (cpid < 0) {
err("fork failed");
return 1;
}
else if (cpid == 0) {
close(child_pipe[0]);
return child(child_pipe[1]);
}
close(child_pipe[1]);
ret = pipe(signal_pipe);
if (ret < 0) {
err("pipe failed");
return 1;
}
spid = test_fork();
if (spid < 0) {
err("Can't fork signal process");
return 1;
} else if (spid == 0) {
close(signal_pipe[1]);
ret = read(signal_pipe[0], &status, sizeof(status));
if (ret != sizeof(status)) {
err("read");
return 1;
}
test_msg("send signal to %d\n", cpid);
ret = kill(cpid, SIGUSR2);
if (ret < 0) {
err("kill failed");
}
return 0;
}
close(signal_pipe[0]);
sh = signal(SIGCHLD, sig_chld_handler);
if (sh == SIG_ERR) {
err("signal failed");
return 1;
}
test_msg("wait while child initialized");
ret = read(child_pipe[0], &status, sizeof(status));
if (ret != sizeof(status)) {
err("read from child process failed\n");
return 1;
}
ret = write(signal_pipe[1], &status, sizeof(status));
if (ret != sizeof(status)) {
err("write to signal process failed");
}
close(signal_pipe[1]);
test_daemon();
test_waitsig();
while (1) {
test_msg("waiting...\n");
pid = wait(&status);
if (pid < 0) {
if (errno != ECHILD)
err("wait");
break;
}
if (WIFSTOPPED(status)) {
siginfo_t siginfo;
test_msg("pid=%d stopsig=%d\n", pid, WSTOPSIG(status));
ret = ptrace(PTRACE_GETSIGINFO, pid, 0, &siginfo);
if (ret < 0) {
err("ptrace failed");
return 1;
} else
test_msg("pid=%d sends signal\n", siginfo.si_pid);
ret = ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
if (ret < 0)
err("ptrace failed");
ret = read(child_pipe[0], &status, sizeof(status));
if (ret != sizeof(status)) {
err("read");
return 1;
}
if (spid != siginfo.si_pid)
fail("%d!=%d", cpid, siginfo.si_pid);
else if (status == siginfo.si_pid)
pass();
else {
fail("%d!=%d", status, siginfo.si_pid);
return 1;
}
}
if (WIFEXITED(status)) {
test_msg("pid = %d status = %d\n", pid, WEXITSTATUS(status));
if (WEXITSTATUS(status))
return 1;
}
if (WIFSTOPPED(status)) {
test_msg("pid = %d signal = %d\n", pid, WTERMSIG(status));
return 1;
}
}
return 0;
}
#!/bin/bash
# $Id: route_rules,v 1.1 2007/06/04 12:11:30 agladkov Exp $
#
# Copyright (c) 2007 by SWsoft.
# All rights reserved.
#
# Description:
# check that routes saved after migration
export PATH=$PATH:${0%/*}/../../lib
die()
{
echo "$0:${BASH_LINENO[0]}: $*" >&2
exit 1
}
fail()
{
echo "FAIL: $0:${BASH_LINENO[0]}: $*" > "$outfile"
exit 1
}
do_or_fail()
{
local failmsg="$1" output
shift
output="$(eval $@ 2>&1)" ||
fail "$failmsg: $output"
}
do_start()
{
[ -f "$statefile" ] && die "state file $statefile aleady exists"
# Get default route
dev_name=`ip route list match 0.0.0.0/0 | sed 's/.*dev \([^ ]*\).*/\1/'`
[ -n "$dev_name" ] || fail "dev_name is zero: " \
"\$dev_name=\`ip route list match 0.0.0.0/0 | " \
"sed 's/.*dev \([^ ]*\).*/\1/'"
do_or_fail "can't add routes" \
ip r a 1.2.3.4/32 dev $dev_name && ip r a 1.2.0.0/16 via 1.2.3.4
do_or_fail "can't list created routes" \
ip r \| grep "1.2.3.4" \> "$statefile"
}
do_stop()
{
do_or_fail "can't compare the routes" \
ip r \| grep "1.2.3.4" \| diff -u "$statefile" -
rm -f "$statefile"
IFS="
";
for line in `ip r | grep "1.2.3.4"`; do
eval ip r del $line
done
echo "PASS" > $outfile
}
tmpargs="$(parseargs.sh --name=$0 \
--flags-req=statefile,outfile \
-- "$@")" ||
die "can't parse command line"
eval "$tmpargs"
[ -f "$outfile" ] && die "out file $outfile aleady exists"
# expect "start" or "stop"
action=${1:?Specify action$(die 'Specify action')}
do_$action
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <errno.h>
#include "zdtmtst.h"
const char *test_doc="Tests detached shmems migrate fine";
const char *test_author="Andrew Vagin <avagin@parallels.com>";
#define DEF_MEM_SIZE (40960)
unsigned int shmem_size = DEF_MEM_SIZE;
TEST_OPTION(shmem_size, uint, "Size of shared memory segment", 0);
#define INIT_CRC (~0)
int main(int argc, char **argv)
{
key_t key;
int shm;
int fail_count = 0;
uint8_t *mem;
uint32_t crc;
int ret;
test_init(argc, argv);
key = ftok(argv[0], 822155666);
if (key == -1) {
err("Can't make key");
goto out;
}
shm = shmget(key, shmem_size, 0777 | IPC_CREAT | IPC_EXCL);
if (shm == -1) {
err("Can't get shm");
fail_count++;
goto out;
}
mem = shmat(shm, NULL, 0);
if (mem == (void *)-1) {
err("Can't attach shm");
fail_count++;
goto out_shm;
}
test_daemon();
crc = INIT_CRC;
datagen(mem, shmem_size, &crc);
ret = shmdt(mem);
if (ret) {
err("Can't detach shm");
fail_count++;
goto out_shm;
}
test_waitsig();
mem = shmat(shm, NULL, 0);
if (mem == (void *)-1) {
err("Can't attach shm");
fail_count++;
goto out_shm;
}
crc = INIT_CRC;
if (datachk(mem, shmem_size, &crc)) {
fail_count++;
fail("shmem data are corrupted");
}
shmdt(mem);
out_shm:
shmctl(shm, IPC_RMID, NULL);
if (fail_count == 0)
pass();
out:
return 0;
}
#include <unistd.h>
#include "zdtmtst.h"
const char *test_doc = "Suspend while migrating";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
int main(int argc, char ** argv)
{
test_init(argc, argv);
test_daemon();
test_waitsig();
pass();
return 0;
}
#include "zdtmtst.h"
const char *test_doc = "static test for AIO\n";
const char *test_author = "Andrew Vagin <avagin@parallels.com>";
/* Description:
* Create two tcp socket, server send asynchronous request on
* read data and clietn write data after migration
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <aio.h>
#include <sys/socket.h>
#include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */
#include <wait.h>
#define PORT 8880
int init_client(char *servIP, unsigned short servPort);
int accept_server(int sock);
int init_server(int port);
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
char buf[BUF_SIZE];
int fd, fd_s;
struct aiocb aiocb;
int status;
pid_t pid;
int ret, res;
const struct aiocb *aioary[1];
test_init(argc, argv);
if ((fd_s = init_server(PORT)) < 0) {
err("initializing server failed");
return 1;
}
pid = test_fork();
if (pid < 0) {
err("fork failed. Return %d %m", pid);
return 1;
}
if (pid == 0) {
/*
* Chiled is client of TCP connection
*/
close(fd_s);
fd = init_client("127.0.0.1", PORT);
if (fd < 0)
return 1;
memset(&aiocb, 0, sizeof(struct aiocb));
aiocb.aio_fildes = fd;
aiocb.aio_buf = buf;
aiocb.aio_nbytes = BUF_SIZE;
ret = aio_read(&aiocb);
if (ret < 0) {
err("aio_read failed %m");
return 1;
}
/* Wait for request completion */
aioary[0] = &aiocb;
ret = aio_error(&aiocb);
#ifdef DEBUG
test_msg(".");
#endif
res = 0;
again:
if (aio_suspend(aioary, 1, NULL) < 0 && errno != EINTR) {
err("aio_suspend failed %m");
res = 1;
}
ret = aio_error(&aiocb);
if (!res && ret == EINPROGRESS) {
#ifdef DEBUG
test_msg("restart aio_suspend\n");
#endif
goto again;
}
if (ret != 0) {
err("Error at aio_error() %s", strerror(ret));
res = 1;
}
if (aio_return(&aiocb) != BUF_SIZE) {
err("Error at aio_return() %m");
res = 1;
}
close(fd);
return res;
}
/*
* parent is server of TCP connection
*/
fd = accept_server(fd_s);
close(fd_s);
if (fd < 0) {
err("can't accept client connection %m");
goto error;
}
test_daemon();
test_waitsig();
if (write(fd, buf, BUF_SIZE) < BUF_SIZE) {
err("can't write");
goto error;
}
close(fd);
if (wait(&status) < 0) {
err("wait failed %m");
goto error;
}
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
err("chiled failed. Return %d", WEXITSTATUS(status));
return 1;
}
pass();
return 0;
error:
kill(pid, SIGKILL);
wait(&status);
return -1;
}
int init_server(int port)
{
struct sockaddr_in addr;
int sock;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htons(INADDR_ANY);
addr.sin_port = htons(port);
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
err ("socket() failed %m");
return -1;
}
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
err ("bind() failed %m");
return -1;
}
if (listen(sock, 1) == -1) {
err ("listen() failed %m");
return -1;
}
return sock;
}
int accept_server(int sock)
{
struct sockaddr_in maddr;
int sock2;
socklen_t addrlen;
#ifdef DEBUG
test_msg ("Waiting for connection..........\n");
#endif
addrlen = sizeof(maddr);
sock2 = accept(sock,(struct sockaddr *) &maddr, &addrlen);
if (sock2 == -1) {
err ("accept() failed %m");
return -1;
}
#ifdef DEBUG
test_msg ("Connection!!\n");
#endif
return sock2;
}
int init_client(char *servIP, unsigned short servPort)
{
int sock;
struct sockaddr_in servAddr;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
err("can't create socket %m");
return -1;
}
/* Construct the server address structure */
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(servIP);
servAddr.sin_port = htons(servPort);
if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) {
err("can't connect to server %m");
return -1;
}
return sock;
}
#include <string.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Start a calculation, leaving SSE in a certain state,\n"
"before migration, continue after";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#if defined(__i386__) || defined(__x86_64__)
void start(float *in)
{
__asm__ volatile (
"movaps %0, %%xmm0\n"
"movaps %1, %%xmm1\n"
"addps %%xmm0, %%xmm1\n"
"sqrtps %%xmm1, %%xmm2\n"
:
: "m" (in[0]), "m" (in[4])
);
}
void finish(float *out)
{
__asm__ volatile (
"movaps %%xmm1, %0\n"
"movaps %%xmm2, %1\n"
: "=m" (out[0]), "=m" (out[4])
);
}
static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
{
__asm__("cpuid"
: "=a" (*eax),
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (op), "c"(0));
}
int chk_proc_sse(void)
{
unsigned int eax, ebx, ecx, edx;
cpuid(1, &eax, &ebx, &ecx, &edx);
return edx & (1 << 25);
}
#endif
int main(int argc, char **argv)
{
float input[8] __attribute__((aligned(16)));
float res1[8], res2[8];
int i;
test_init(argc, argv);
#if defined(__i386__) || defined(__x86_64__)
if (!chk_proc_sse()) {
skip("SSE not supported");
return 1;
}
for (i = 0; i < sizeof(input) / sizeof(float); i++)
input[i] = drand48();
start(input);
finish(res1);
start(input);
finish(res1);
test_daemon();
test_waitsig();
finish(res2);
if (memcmp((uint8_t *) res1, (uint8_t *) res2, sizeof(res1)))
fail("results differ\n");
else
pass();
#else
skip("Unsupported arch");
#endif
return 0;
}
This diff is collapsed.
This diff is collapsed.
#include <sys/stat.h>
#include "zdtmtst.h"
const char *test_doc = "Check that umask didn't change";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
unsigned int mask;
TEST_OPTION(mask, uint, "umask", 1);
int main(int argc, char **argv)
{
unsigned int cur_mask, mask2;
test_init(argc, argv);
cur_mask = umask(mask);
test_daemon();
test_waitsig();
mask2 = umask(0);
if (mask != mask2)
fail("mask changed: %o != %o\n", mask, mask2);
else
pass();
umask(cur_mask);
return 0;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment