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;
}
#include <string.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Start a calculation, leaving SSE2 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(double *in)
{
__asm__ volatile (
"movapd %0, %%xmm0\n"
"movapd %1, %%xmm1\n"
"addpd %%xmm0, %%xmm1\n"
"sqrtpd %%xmm1, %%xmm2\n"
:
: "m" (in[0]), "m" (in[2])
);
}
void finish(double *out)
{
__asm__ volatile (
"movapd %%xmm1, %0\n"
"movapd %%xmm2, %1\n"
: "=m" (out[0]), "=m" (out[2])
);
}
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_sse2(void)
{
unsigned int eax, ebx, ecx, edx;
cpuid(1, &eax, &ebx, &ecx, &edx);
return edx & (1 << 26);
}
#endif
int main(int argc, char **argv)
{
double input[4] __attribute__((aligned(16)));
double res1[4], res2[4];
int i;
test_init(argc, argv);
#if defined(__i386__) || defined(__x86_64__)
if (!chk_proc_sse2()) {
skip("SSE2 not supported");
return 1;
}
for (i = 0; i < sizeof(input) / sizeof(double); i++)
input[i] = drand48();
start(input);
finish(res1);
start(input);
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;
}
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include "zdtmtst.h"
const char *test_doc = "Checks timers keep ticking after migration\n";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
static struct {
const int timer_type;
const int signal;
volatile sig_atomic_t count;
} timer_tests[] = { /* from slowest to fastest */
{ ITIMER_VIRTUAL, SIGVTALRM },
{ ITIMER_PROF, SIGPROF },
{ ITIMER_REAL, SIGALRM },
};
#define NUM_TIMERS (sizeof(timer_tests) / sizeof(timer_tests[0]))
#define MAX_TIMER_COUNT 10
static void timer_tick(int sig)
{
int i;
for (i = 0; i < NUM_TIMERS; i++)
if (timer_tests[i].signal == sig) {
/* don't go beyond MAX_TIMER_COUNT, to avoid overflow */
if (timer_tests[i].count < MAX_TIMER_COUNT)
timer_tests[i].count++;
break;
}
}
static void setup_timers(void)
{
int i;
struct itimerval tv = {
.it_interval = {
.tv_sec = 0,
.tv_usec = 100000
},
.it_value = {
.tv_sec = 0,
.tv_usec = 100
},
};
for (i = 0; i < NUM_TIMERS; i++) {
if (signal(timer_tests[i].signal, timer_tick) == SIG_ERR) {
err("can't set signal handler %d", i);
exit(1);
}
if (setitimer(timer_tests[i].timer_type, &tv, NULL) < 0) {
err("can't set timer %d", i);
exit(1);
}
}
}
static void check_timers(void)
{
int i;
volatile unsigned int j; /* avoid optimizing the loop away */
for (i = 0; i < NUM_TIMERS; i++) /* reset counters first */
timer_tests[i].count = 0;
/* waste some real and CPU time: run for MAX_TIMER_COUNT ticks or until
* j overflows */
for (j = 1; j && timer_tests[0].count < MAX_TIMER_COUNT; j++);
for (i = 0; i < NUM_TIMERS; i++)
if (!timer_tests[i].count) {
fail("timer %d stuck", i);
return;
}
pass();
}
int main(int argc, char **argv)
{
test_init(argc, argv);
setup_timers();
test_daemon();
test_waitsig();
check_timers();
return 0;
}
#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;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "zdtmtst.h"
const char *test_doc = "Create a socket before migration, and bind to it after\n";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
#define TEST_PORT 59687
#define TEST_ADDR INADDR_ANY
int main(int argc, char ** argv)
{
int sock;
struct sockaddr_in name = {
.sin_family = AF_INET,
.sin_port = htons(TEST_PORT),
.sin_addr.s_addr = htonl(TEST_ADDR),
};
test_init(argc, argv);
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
err("can't create socket: %m");
return 1;
}
test_daemon();
test_waitsig();
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
fail("can't bind to a socket: %m");
else
pass();
close(sock);
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 and then unlinked";
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;
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;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m", filename);
return 1;
}
test_daemon();
test_waitsig();
if (close(fd) < 0) {
fail("can't close %s: %m", filename);
return 1;
}
pass();
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, "
"opened in WRONLY mode and then unlinked";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char **argv)
{
int fd, fd1;
mode_t mode = S_IFIFO | 0600;
test_init(argc, argv);
if (mknod(filename, mode, 0)) {
err("can't make fifo \"%s\": %m\n", filename);
exit(1);
}
fd = open(filename, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
err("open(%s, O_RDONLY | O_NONBLOCK) Failed: %m\n",
filename);
return 1;
}
fd1 = open(filename, O_WRONLY);
if (fd1 < 0) {
err("open(%s, O_WRONLY) Failed: %m\n", filename);
return 1;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m", filename);
return 1;
}
test_daemon();
test_waitsig();
if (close(fd) < 0) {
fail("can't close (O_RDONLY | O_NONBLOCK) %s: %m", filename);
return 1;
}
if (close(fd1) < 0) {
fail("can't close (O_WRONLY) %s: %m", filename);
return 1;
}
pass();
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Open, unlink, change size, seek, migrate, check size";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char ** argv)
{
int fd;
size_t fsize=1000;
mode_t mode;
uid_t uid;
gid_t gid;
uint8_t buf[fsize];
struct stat fst;
test_init(argc, argv);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
if (fstat(fd, &fst) < 0) {
err("can't get file info %s before: %m\n", filename);
goto failed;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m\n", filename);
goto failed;
}
/* Change file size */
if (fst.st_size != 0) {
err("%s file size eq %d\n", fst.st_size);
goto failed;
}
memset(buf, '0', sizeof(buf));
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
err("can't write %s: %m\n", filename);
goto failed;
}
/* Change file mode */
if ((fst.st_mode & S_IXOTH) == 0)
mode = (fst.st_mode | S_IXOTH);
else
mode = (fst.st_mode ^ S_IXOTH);
if (fchmod(fd, mode) < 0) {
err("can't chmod %s: %m\n", filename);
goto failed;
}
/* Change uid, gid */
if (fchown(fd, (uid = fst.st_uid + 1), (gid = fst.st_gid + 1)) < 0) {
err("can't chown %s: %m\n", filename);
goto failed;
}
if (lseek(fd, 0, SEEK_SET) != 0) {
err("can't reposition to 0: %m");
goto failed;
}
test_daemon();
test_waitsig();
if (fstat(fd, &fst) < 0) {
err("can't get %s file info after: %m\n", filename);
goto failed;
}
/* Check file size */
if (fst.st_size != fsize) {
fail("(via fstat): file size changed to %d", fst.st_size);
goto failed;
}
fst.st_size = lseek(fd, 0, SEEK_END);
if (fst.st_size != fsize) {
fail("(via lseek): file size changed to %d", fst.st_size);
goto failed;
}
/* Check mode */
if (fst.st_mode != mode) {
fail("mode is changed to %o(%o)", fst.st_mode, mode);
goto failed;
}
/* Check uid, gid */
if (fst.st_uid != uid || fst.st_gid != gid) {
fail("u(g)id changed: uid=%d(%d), gid=%d(%d)",
fst.st_uid, uid, fst.st_gid, gid);
goto failed;
}
close(fd);
pass();
return 0;
failed:
unlink(filename);
close(fd);
return 1;
}
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Open, unlink, change size, migrate, check size";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char ** argv)
{
int fd;
size_t fsize=1000;
uint8_t buf[fsize];
struct stat fst;
test_init(argc, argv);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
if (fstat(fd, &fst) < 0) {
err("can't get file info %s before: %m\n", filename);
goto failed;
}
if (fst.st_size != 0) {
err("%s file size eq %d\n", fst.st_size);
goto failed;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m\n", filename);
goto failed;
}
memset(buf, '0', sizeof(buf));
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
err("can't write %s: %m\n", filename);
goto failed;
}
test_daemon();
test_waitsig();
if (fstat(fd, &fst) < 0) {
err("can't get %s file info after: %m\n", filename);
goto failed;
}
if (fst.st_size != fsize) {
fail("(via fstat): file size changed to %d", fst.st_size);
goto failed;
}
fst.st_size = lseek(fd, 0, SEEK_END);
if (fst.st_size != fsize) {
fail("(via lseek): file size changed to %d", fst.st_size);
goto failed;
}
close(fd);
pass();
return 0;
failed:
unlink(filename);
close(fd);
return 1;
}
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "Checkpointing/restore of big (2Gb) unlinked files";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char ** argv)
{
int fd;
char buf[1000000];
off64_t offset= 0x80002000ULL;
size_t count;
test_init(argc, argv);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
if (lseek64(fd, offset, SEEK_SET) < 0) {
err("can't lseek %s, offset= %x: %m\n", filename,
offset);
goto failed;
}
count = sizeof(buf);
memset(buf, 0, count);
if (write(fd, buf, count) != count) {
err("can't write %s: %m\n", filename);
goto failed;
}
if (unlink(filename) < 0) {
err("can't unlink %s: %m\n", filename);
goto failed;
}
test_daemon();
test_waitsig();
close(fd);
pass();
return 0;
failed:
unlink(filename);
close(fd);
return 1;
}
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "See if we can wait() for a child after migration";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
int main(int argc, char ** argv)
{
int ret;
pid_t pid;
test_init(argc, argv);
pid = fork();
if (pid < 0) {
err("fork failed: %m\n");
exit(1);
}
if (pid == 0) {
test_waitsig();
_exit(0);
}
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: %m\n");
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;
}
pass();
out:
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Write file before migration, read after";
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;
uint32_t crc;
uint8_t buf[1000000];
test_init(argc, argv);
fd = open(filename, O_WRONLY | 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 %s: %m\n", filename);
exit(1);
}
close(fd);
test_daemon();
test_waitsig();
fd = open(filename, O_RDONLY);
if (fd < 0) {
fail("can't open %s: %m\n", filename);
exit(1);
}
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;
}
pass();
out:
unlink(filename);
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Write and half way read file before migration, complete after";
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;
int len;
uint32_t crc = ~0;
uint8_t buf[1000000];
test_init(argc, argv);
fd = open(filename, O_WRONLY | 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 %s: %m\n", filename);
exit(1);
}
close(fd);
fd = open(filename, O_RDONLY);
if (fd < 0) {
err("can't open %s: %m\n", filename);
exit(1);
}
len = sizeof(buf) / 2;
if (read(fd, buf, len) != len) {
err("can't read %s: %m\n", filename);
exit(1);
}
test_daemon();
test_waitsig();
/* recover reading */
if (read(fd, buf + len, sizeof(buf) - len) != (sizeof(buf) - len)) {
fail("can't read %s: %m\n", filename);
goto out;
}
crc = ~0;
if (datachk(buf, sizeof(buf), &crc)) {
fail("CRC mismatch\n");
goto out;
}
pass();
out:
unlink(filename);
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc = "Write file half way before migration, complete and read after";
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, fd1;
int len, full_len;
uint32_t crc;
uint8_t buf[1000000];
char str[32];
test_init(argc, argv);
fd = open(filename, O_WRONLY | 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);
full_len = sizeof(buf);
// create standard file
sprintf(str, "standard_%s", filename);
fd1 = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (write(fd1, buf, full_len) != full_len) {
err("can't write %s: %m\n", str);
exit(1);
}
close(fd1);
len = sizeof(buf) / 2;
if (write(fd, buf, len) != len) {
err("can't write %s: %m\n", filename);
exit(1);
}
test_daemon();
test_waitsig();
if (write(fd, buf + len, sizeof(buf) - len) != (sizeof(buf) - len)) {
fail("can't write %s: %m\n", filename);
goto out;
}
close(fd);
fd = open(filename, O_RDONLY);
if (fd < 0) {
fail("can't open %s: %m\n", filename);
return 1;
}
if (read(fd, buf, full_len) != full_len) {
fail("can't read %s: %m\n", filename);
return 1;
}
crc = ~0;
if (datachk(buf, full_len, &crc)) {
fail("CRC mismatch\n");
return 1;
}
pass();
out:
unlink(filename);
return 0;
}
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include "zdtmtst.h"
const char *test_doc = "Open r/w and unlink file, and fork before migration;\n"
"check that the child can write to it and the parent\n"
"can read from it after 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, ret;
pid_t pid;
uint32_t crc;
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);
}
if (unlink(filename)) {
err("can't unlink %s: %m\n", filename);
exit(1);
}
pid = fork();
if (pid < 0) {
err("can't fork: %m\n");
exit(1);
}
if (pid == 0) { /* child writes to the unlinked file and returns */
test_waitsig();
crc = ~0;
datagen(buf, sizeof(buf), &crc);
if (write(fd, buf, sizeof(buf)) != sizeof(buf))
_exit(errno);
close(fd);
_exit(0);
}
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 (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 (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 <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include "zdtmtst.h"
const char *test_doc = "See if we can wait() for a zombified child after migration";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
int main(int argc, char ** argv)
{
int ret;
pid_t pid;
test_init(argc, argv);
pid = fork();
if (pid < 0) {
err("fork failed: %m\n");
exit(1);
}
if (pid == 0)
_exit(0);
test_daemon();
test_waitsig();
if (wait(&ret) != pid) {
fail("wait() returned wrong pid: %m\n");
exit(1);
}
if (WIFEXITED(ret)) {
ret = WEXITSTATUS(ret);
if (ret) {
fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
exit(1);
}
}
if (WIFSIGNALED(ret)) {
fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
exit(1);
}
pass();
return 0;
}
LIBDIR = ../../lib
LIB = $(LIBDIR)/libzdtmtst.a
override CPPFLAGS += -I$(LIBDIR)
CFLAGS = -g -O2 -Wall
TST_NOFILE = \
pipe_loop00 \
pipe_shared00 \
socket_loop00 \
netlink00 \
file_aio \
TST_FILE = \
unix_sock \
fifo_dyn \
fifo_loop \
TST = $(TST_NOFILE) $(TST_FILE)
SRC = $(TST:%=%.c)
OBJ = $(SRC:%.c=%.o)
DEP = $(SRC:%.c=%.d)
PID = $(TST:%=%.pid)
OUT = $(TST:%=%.out)
DEPEND.c = $(COMPILE.c) -MM -MP
%.d: %.c
$(DEPEND.c) $(OUTPUT_OPTION) $<
all: $(TST)
install: all
$(TST_NOFILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out
$(TST_FILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --filename=$<.test
%.out: %.pid %
-kill -TERM `< $<`
start: $(PID)
%.is_running: %.pid
kill -0 `< $<`
check_start: $(PID:%.pid=%.is_running)
stop:
-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)
file_aio: override LDLIBS += -lrt
$(LIB): force
$(MAKE) -C $(LIBDIR)
clean:
$(RM) $(OBJ) $(TST) *~
cleandep: clean
$(RM) $(DEP)
cleanout:
$(RM) *.pid *.out* *.test*
realclean: cleandep cleanout
.PHONY: force clean cleandep cleanout realclean start check_start stop wait_stop
-include $(DEP)
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "dynamic FIFO test";
#define PROCS_DEF 2 /* 0 - parent, 1 - child */
#define BUF_SIZE 256
unsigned int num_procs = PROCS_DEF;
char *filename;
TEST_OPTION(filename, string, "file name", 1);
int main(int argc, char **argv)
{
int ret = 0;
int readfd, writefd;
mode_t mode = S_IFIFO | 0600;
char path[PROCS_DEF][BUF_SIZE];
pid_t pid;
int i;
uint8_t buf[0x100000];
int chret;
char *file_path;
test_init(argc, argv);
for (i = 0; i < PROCS_DEF; i++) {
file_path = path[i];
if (snprintf(file_path, BUF_SIZE, "%s-%02d", filename, i) >= BUF_SIZE) {
err("filename %s is too long\n", filename);
exit(1);
}
if (mkfifo(file_path, mode)) {
err("can't make fifo \"%s\": %m\n", file_path);
exit(1);
}
}
pid = test_fork();
if (pid < 0) {
err("Can't fork: %m\n");
kill(0, SIGKILL);
exit(1);
}
if (pid == 0) {
file_path = path[0];
readfd = open(file_path, O_RDONLY);
if (readfd < 0) {
err("open(%s, O_RDONLY) Failed: %m\n", file_path);
ret = errno;
return ret;
}
file_path = path[1];
writefd = open(file_path, O_WRONLY);
if (writefd < 0) {
err("open(%s, O_WRONLY) Failed: %m\n", file_path);
ret = errno;
return ret;
}
if (pipe_in2out(readfd, writefd, buf, sizeof(buf)) < 0)
/* pass errno as exit code to the parent */
if (test_go() /* signal NOT delivered */ ||
(errno != EINTR && errno != EPIPE))
ret = errno;
close(readfd);
close(writefd);
exit(ret);
}
file_path = path[0];
writefd = open(file_path, O_WRONLY);
if (writefd < 0) {
err("open(%s, O_WRONLY) Failed: %m\n", file_path);
kill(pid, SIGKILL);
return 1;
}
file_path = path[1];
readfd = open(file_path, O_RDONLY);
if (readfd < 0) {
err("open(%s, O_RDONLY) Failed: %m\n", file_path);
kill(pid, SIGKILL);
return 1;
}
test_daemon();
while (test_go()) {
int len, rlen = 0, wlen;
uint8_t rbuf[sizeof(buf)], *p;
datagen(buf, sizeof(buf), NULL);
wlen = write(writefd, buf, sizeof(buf));
if (wlen < 0) {
if (errno == EINTR)
continue;
else {
fail("write failed: %m\n");
ret = 1;
break;
}
}
for (p = rbuf, len = wlen; len > 0; p += rlen, len -= rlen) {
rlen = read(readfd, p, len);
if (rlen <= 0)
break;
}
if (rlen < 0 && errno == EINTR)
continue;
if (len > 0) {
fail("read failed: %m\n");
ret = 1;
break;
}
if (memcmp(buf, rbuf, wlen)) {
fail("data mismatch\n");
ret = 1;
break;
}
}
close(writefd);
test_waitsig();
wait(&chret);
chret = WEXITSTATUS(chret);
if (chret) {
fail("child exited with non-zero code %d (%s)\n",
chret, strerror(chret));
return 1;
}
if (!ret)
pass();
close(readfd);
for (i = 0; i < PROCS_DEF; i++)
unlink(path[i]);
return 0;
}
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Multi-process fifo loop";
#define BUF_SIZE 256
#define PROCS_DEF 4
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes to create "
"(default " __stringify(PROCS_DEF) ")", 0);
char *filename;
TEST_OPTION(filename, string, "file name", 1);
static int pids[PROCS_DEF];
volatile sig_atomic_t num_exited = 0;
void inc_num_exited(int signo)
{
num_exited++;
}
int main(int argc, char **argv)
{
int ret = 0;
int readfd, writefd;
mode_t mode = S_IFIFO | 0644;
char path[PROCS_DEF][BUF_SIZE];
pid_t pid;
int i;
uint8_t buf[0x100000];
char *file_path;
test_init(argc, argv);
for (i = 0; i < PROCS_DEF; i++) {
file_path = path[i];
if (snprintf(file_path, BUF_SIZE, "%s-%02d", filename, i) >= BUF_SIZE) {
err("filename %s is too long\n", filename);
exit(1);
}
if (mkfifo(file_path, mode)) {
err("can't make fifo \"%s\": %m\n", file_path);
exit(1);
}
}
if (signal(SIGCHLD, inc_num_exited) == SIG_ERR) {
err("can't set SIGCHLD handler: %m\n");
exit(1);
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
pid = test_fork();
if (pid < 0) {
err("Can't fork: %m\n");
kill(0, SIGKILL);
exit(1);
}
if (pid == 0) {
file_path = path[i - 1];
readfd = open(file_path, O_RDONLY);
if (readfd < 0) {
err("open(%s, O_RDONLY) Failed: %m\n",
file_path);
ret = errno;
return ret;
}
file_path = path[i];
writefd = open(file_path, O_WRONLY);
if (writefd < 0) {
err("open(%s, O_WRONLY) Failed: %m\n",
file_path);
ret = errno;
return ret;
}
signal(SIGPIPE, SIG_IGN);
if (pipe_in2out(readfd, writefd, buf, sizeof(buf)) < 0)
/* pass errno as exit code to the parent */
if (test_go() /* signal NOT delivered */ ||
(errno != EINTR && errno != EPIPE))
ret = errno;
close(readfd);
close(writefd);
exit(ret);
}
pids[i] = pid;
}
file_path = path[0];
writefd = open(file_path, O_WRONLY);
if (writefd < 0) {
err("open(%s, O_WRONLY) Failed: %m\n", file_path);
kill(0, SIGKILL);
exit(1);
}
file_path = path[i - 1];
readfd = open(file_path, O_RDONLY);
if (readfd < 0) {
err("open(%s, O_RDONLY) Failed: %m\n", file_path);
kill(0, SIGKILL);
exit(1);
}
if (num_exited) {
err("Some children died unexpectedly\n");
kill(0, SIGKILL);
exit(1);
}
test_daemon();
while (test_go()) {
int len, rlen = 0, wlen;
uint8_t rbuf[sizeof(buf)], *p;
datagen(buf, sizeof(buf), NULL);
wlen = write(writefd, buf, sizeof(buf));
if (wlen < 0) {
if (errno == EINTR)
continue;
else {
fail("write failed: %m\n");
ret = 1;
break;
}
}
for (p = rbuf, len = wlen; len > 0; p += rlen, len -= rlen) {
rlen = read(readfd, p, len);
if (rlen <= 0)
break;
}
if (rlen < 0 && errno == EINTR)
continue;
if (len > 0) {
fail("read failed: %m\n");
ret = 1;
break;
}
if (memcmp(buf, rbuf, wlen)) {
fail("data mismatch\n");
ret = 1;
break;
}
}
close(writefd);
test_waitsig(); /* even if failed, wait for migration to complete */
if (kill(0, SIGTERM)) {
fail("failed to send SIGTERM to my process group: %m\n");
return 1; /* shouldn't wait() in this case */
}
close(readfd);
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
int chret;
if (waitpid(pids[i], &chret, 0) < 0) {
fail("waitpid error: %m\n");
ret = 1;
continue;
}
chret = WEXITSTATUS(chret);
if (chret) {
fail("child %d exited with non-zero code %d (%s)\n",
i, chret, strerror(chret));
ret = 1;
continue;
}
}
if (!ret)
pass();
for (i = 0; i < PROCS_DEF; i++)
unlink(path[i]);
return 0;
}
#include "zdtmtst.h"
const char *test_doc = "test for AIO";
const char *test_author = "Andrew Vagin <avagin@parallels.com>";
#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>
#define BUF_SIZE 1024
int main(int argc, char **argv)
{
test_init(argc, argv);
char buf[BUF_SIZE];
int fd;
struct aiocb aiocb;
const struct aiocb *aioary[1];
char tmpfname[256]="/tmp/file_aio.XXXXXX";
int ret;
fd = mkstemp(tmpfname);
if (fd == -1) {
err("Error at open(): %s", strerror(errno));
exit(1);
}
unlink(tmpfname);
if (write(fd, buf, BUF_SIZE) != BUF_SIZE) {
err("Error at write(): %s",
strerror(errno));
exit(1);
}
test_daemon();
while (test_go()) {
memset(&aiocb, 0, sizeof(struct aiocb));
aiocb.aio_offset = 0;
aiocb.aio_fildes = fd;
aiocb.aio_buf = buf;
aiocb.aio_nbytes = BUF_SIZE;
ret = aio_read(&aiocb);
if (ret < 0) {
if ((errno == EINTR) && (!test_go()))
break;
err("aio_read failed %m");
return 1;
}
if (ret < 0) {
err("aio_read failed %s\n", strerror(errno));
exit(1);
}
/* Wait for request completion */
aioary[0] = &aiocb;
again:
ret = aio_suspend(aioary, 1, NULL);
if (ret < 0) {
if ((errno == EINTR) && (! test_go()))
break;
if (errno != EINTR) {
err("aio_suspend failed %m");
return 1;
}
}
ret = aio_error(&aiocb);
if (ret == EINPROGRESS) {
#ifdef DEBUG
test_msg("restart aio_suspend\n");
#endif
goto again;
}
if (ret != 0) {
err("Error at aio_error() %s", strerror(ret));
return 1;
}
ret = aio_return(&aiocb);
if (ret < 0) {
if ((errno == EINTR) && (!test_go()))
break;
err("aio_return failed %m");
return 1;
}
if (ret != BUF_SIZE) {
err("Error at aio_return()\n");
exit(1);
}
}
close(fd);
pass();
return 0;
}
/* Description: testcase for netlink sockets migration.
* e.g.
* ip rule show
* ip rule add
* ip rule show
* ip rule del
* in a loop
*/
#include <asm/types.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <linux/netlink.h>
#include <string.h>
#include <linux/rtnetlink.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include "zdtmtst.h"
#undef DEBUG
//#define DEBUG
const char *test_doc = "Netlink socket loop";
const char *test_author = "Andrew Vagin (avagin@parallels.com)";
//buffer to hold the RTNETLINK request
struct {
struct nlmsghdr nl;
struct rtmsg rt;
char buf[8192];
} req;
// variables used for
// socket communications
int fd;
struct sockaddr_nl la;
struct sockaddr_nl pa;
struct msghdr msg;
struct iovec iov;
int rtn;
// buffer to hold the RTNETLINK reply(ies)
char buf[8192];
char dsts[24] = "192.168.0.255";
int pn = 32;//network prefix
// RTNETLINK message pointers & lengths
// used when processing messages
struct nlmsghdr *nlp;
int nll;
struct rtmsg *rtp;
int rtl;
struct rtattr *rtap;
int send_request();
int recv_reply();
int form_request_add();
int form_request_del();
int read_reply();
typedef int (*cmd_t)();
#define CMD_NUM 2
cmd_t cmd[CMD_NUM]={form_request_add, form_request_del};
int main(int argc, char *argv[])
{
int ret=0;
int i;
test_init(argc, argv);
test_daemon();
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd<0){
err("socket: %m ");
goto out;
}
// setup local address & bind using
// this address
bzero(&la, sizeof(la));
la.nl_family = AF_NETLINK;
la.nl_pid = getpid();
if (bind(fd, (struct sockaddr*) &la, sizeof(la))){
err("bind failed: %m ");
ret=-1;
goto out;
}
//Preperation:
form_request_del();
send_request();
recv_reply();
while (test_go()){
for (i=0;i<CMD_NUM;i++){
cmd[i]();
if (send_request()<0){
if ((errno == EINTR) && !test_go())
goto pass;
fail("send_request failed");
goto out;
};
if (recv_reply()<0){
if ((errno == EINTR) && !test_go())
goto pass;
fail("RTNETLINK answers: %m");
goto out;
};
#ifdef DEBUG
if (read_reply()<0){
if ((errno == EINTR) && !test_go())
goto pass;
fail("read_reply failed");
goto out;
}
#endif
}
}
test_waitsig();
pass:
pass();
out:
return 0;
}
int send_request()
{
// create the remote address
// to communicate
bzero(&pa, sizeof(pa));
pa.nl_family = AF_NETLINK;
// initialize & create the struct msghdr supplied
// to the sendmsg() function
bzero(&msg, sizeof(msg));
msg.msg_name = (void *) &pa;
msg.msg_namelen = sizeof(pa);
// place the pointer & size of the RTNETLINK
// message in the struct msghdr
iov.iov_base = (void *) &req.nl;
iov.iov_len = req.nl.nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// send the RTNETLINK message to kernel
rtn = sendmsg(fd, &msg, 0);
if (rtn<0){
err("sendmsg failed: %m");
return -1;
}
return 0;
}
int recv_reply()
{
char *p;
// initialize the socket read buffer
bzero(buf, sizeof(buf));
p = buf;
nll = 0;
// read from the socket until the NLMSG_DONE is
// returned in the type of the RTNETLINK message
// or if it was a monitoring socket
while(1) {
rtn = recv(fd, p, sizeof(buf) - nll, 0);
if (rtn<0) {
err("recv failed: %m");
return -1;
}
if (rtn == 0) {
err("EOF on netlink\n");
return -1;
}
nlp = (struct nlmsghdr *) p;
if(nlp->nlmsg_type == NLMSG_DONE)
return 0;
if (nlp->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlp);
errno=-err->error;
if (errno) {
return -1;
}
return 0;
}
// increment the buffer pointer to place
// next message
p += rtn;
// increment the total size by the size of
// the last received message
nll += rtn;
if((la.nl_groups & RTMGRP_IPV4_ROUTE)
== RTMGRP_IPV4_ROUTE)
break;
}
return 0;
}
int read_reply()
{
//string to hold content of the route
// table (i.e. one entry)
char dsts[24], gws[24], ifs[16], ms[24];
// outer loop: loops thru all the NETLINK
// headers that also include the route entry
// header
nlp = (struct nlmsghdr *) buf;
for(;NLMSG_OK(nlp, nll);nlp=NLMSG_NEXT(nlp, nll))
{
// get route entry header
rtp = (struct rtmsg *) NLMSG_DATA(nlp);
// we are only concerned about the
// main route table
if(rtp->rtm_table != RT_TABLE_MAIN)
continue;
// init all the strings
bzero(dsts, sizeof(dsts));
bzero(gws, sizeof(gws));
bzero(ifs, sizeof(ifs));
bzero(ms, sizeof(ms));
// inner loop: loop thru all the attributes of
// one route entry
rtap = (struct rtattr *) RTM_RTA(rtp);
rtl = RTM_PAYLOAD(nlp);
for(;RTA_OK(rtap, rtl);rtap=RTA_NEXT(rtap,rtl))
{
switch(rtap->rta_type)
{
// destination IPv4 address
case RTA_DST:
inet_ntop(AF_INET, RTA_DATA(rtap),
dsts, 24);
break;
// next hop IPv4 address
case RTA_GATEWAY:
inet_ntop(AF_INET, RTA_DATA(rtap),
gws, 24);
break;
// unique ID associated with the network
// interface
case RTA_OIF:
sprintf(ifs, "%d",
*((int *) RTA_DATA(rtap)));
default:
break;
}
}
sprintf(ms, "%d", rtp->rtm_dst_len);
test_msg("dst %s/%s gw %s if %s\n",
dsts, ms, gws, ifs);
}
return 0;
}
int form_request_add()
{
// attributes of the route entry
int ifcn = 1; //interface number
// initialize RTNETLINK request buffer
bzero(&req, sizeof(req));
// compute the initial length of the
// service request
rtl = sizeof(struct rtmsg);
// add first attrib:
// set destination IP addr and increment the
// RTNETLINK buffer size
rtap = (struct rtattr *) req.buf;
rtap->rta_type = RTA_DST;
rtap->rta_len = sizeof(struct rtattr) + 4;
inet_pton(AF_INET, dsts,
((char *)rtap) + sizeof(struct rtattr));
rtl += rtap->rta_len;
// add second attrib:
// set ifc index and increment the size
rtap = (struct rtattr *) (((char *)rtap)
+ rtap->rta_len);
rtap->rta_type = RTA_OIF;//Output interface index
rtap->rta_len = sizeof(struct rtattr) + 4;
memcpy(((char *)rtap) + sizeof(struct rtattr),
&ifcn, sizeof(int));
rtl += rtap->rta_len;
// setup the NETLINK header
req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
req.nl.nlmsg_type = RTM_NEWROUTE;
// setup the service header (struct rtmsg)
req.rt.rtm_family = AF_INET;
req.rt.rtm_table = RT_TABLE_MAIN;
req.rt.rtm_protocol = RTPROT_STATIC;
req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
req.rt.rtm_type = RTN_UNICAST;
// set the network prefix size
req.rt.rtm_dst_len = pn;
return 0;
}
int form_request_del()
{
// attributes of the route entry
// initialize RTNETLINK request buffer
bzero(&req, sizeof(req));
// compute the initial length of the
// service request
rtl = sizeof(struct rtmsg);
// add first attrib:
// set destination IP addr and increment the
// RTNETLINK buffer size
rtap = (struct rtattr *) req.buf;
rtap->rta_type = RTA_DST;
rtap->rta_len = sizeof(struct rtattr) + 4;
inet_pton(AF_INET, dsts,
((char *)rtap) + sizeof(struct rtattr));
rtl += rtap->rta_len;
// setup the NETLINK header
req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
req.nl.nlmsg_type = RTM_DELROUTE;
// setup the service header (struct rtmsg)
req.rt.rtm_family = AF_INET;
req.rt.rtm_table = RT_TABLE_MAIN;
req.rt.rtm_protocol = RTPROT_STATIC;
req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
req.rt.rtm_type = RTN_UNICAST;
// set the network prefix size
req.rt.rtm_dst_len = pn;
return 0;
}
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "zdtmtst.h"
const char *test_doc = "Multi-process pipe loop";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#define PROCS_DEF 4
#define PROCS_MAX 64
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes to create "
"(default " __stringify(PROCS_DEF)
", max " __stringify(PROCS_MAX) ")", 0);
volatile sig_atomic_t num_exited = 0;
void inc_num_exited(int signo)
{
num_exited++;
}
int main(int argc, char **argv)
{
int ret = 0;
pid_t pid;
int i;
uint8_t buf[0x100000];
int pipes[PROCS_MAX * 2];
int in, out;
test_init(argc, argv);
if (num_procs > PROCS_MAX) {
err("%d processes is too many: max = %d\n", num_procs, PROCS_MAX);
exit(1);
}
for (i = 0; i < num_procs; i++)
if (pipe(pipes + i * 2)) {
err("Can't create pipes: %m\n");
exit(1);
}
if (signal(SIGCHLD, inc_num_exited) == SIG_ERR) {
err("can't set SIGCHLD handler: %m\n");
exit(1);
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
pid = test_fork();
if (pid < 0) {
err("Can't fork: %m\n");
kill(0, SIGKILL);
exit(1);
}
if (pid == 0) {
int j;
in = i * 2;
out = in - 1;
for (j = 0; j < num_procs * 2; j++)
if (j != in && j != out)
close(pipes[j]);
signal(SIGPIPE, SIG_IGN);
if (pipe_in2out(pipes[in], pipes[out], buf, sizeof(buf)) < 0)
/* pass errno as exit code to the parent */
if (test_go() /* signal NOT delivered */ ||
(errno != EINTR && errno != EPIPE))
ret = errno;
test_waitsig(); /* even if failed, wait for migration to complete */
close(pipes[in]);
close(pipes[out]);
exit(ret);
}
}
for (i = 1; i < num_procs * 2 - 1; i++)
close(pipes[i]);
in = pipes[0];
out = pipes[num_procs * 2 - 1];
/* don't block on writing, _do_ block on reading */
if (set_nonblock(out,1) < 0) {
err("setting O_NONBLOCK failed: %m");
exit(1);
}
if (num_exited) {
err("Some children died unexpectedly\n");
kill(0, SIGKILL);
exit(1);
}
test_daemon();
while (test_go()) {
int len, rlen = 0, wlen;
uint8_t rbuf[sizeof(buf)], *p;
datagen(buf, sizeof(buf), NULL);
wlen = write(out, buf, sizeof(buf));
if (wlen < 0) {
if (errno == EINTR)
continue;
else {
fail("write failed: %m\n", i);
ret = 1;
break;
}
}
for (p = rbuf, len = wlen; len > 0; p += rlen, len -= rlen) {
rlen = read(in, p, len);
if (rlen <= 0)
break;
}
if (rlen < 0 && errno == EINTR)
continue;
if (len > 0) {
fail("read failed: %m\n");
ret = 1;
break;
}
if (memcmp(buf, rbuf, wlen)) {
fail("data mismatch\n");
ret = 1;
break;
}
}
close(out);
test_waitsig(); /* even if failed, wait for migration to complete */
if (kill(0, SIGTERM)) {
fail("failed to send SIGTERM to my process group: %m\n");
goto out; /* shouldn't wait() in this case */
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
int chret;
if (wait(&chret) < 0) {
fail("can't wait for a child: %m\n");
ret = 1;
continue;
}
chret = WEXITSTATUS(chret);
if (chret) {
fail("child %d exited with non-zero code %d (%s)\n",
i, chret, strerror(chret));
ret = 1;
continue;
}
}
if (!ret)
pass();
out:
close(in);
return 0;
}
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "zdtmtst.h"
const char *test_doc = "Multi-process pipe split";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#define PROCS_DEF 4
#define PROCS_MAX 64
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes to create "
"(default " __stringify(PROCS_DEF)
", max " __stringify(PROCS_MAX) ")", 0);
volatile sig_atomic_t num_exited = 0;
void inc_num_exited(int signo)
{
num_exited++;
}
#define SND_CHR 'y'
int main(int argc, char **argv)
{
int ret = 0;
pid_t pid;
int i;
uint8_t buf[PIPE_BUF * 100];
int pipes[2];
test_init(argc, argv);
if (num_procs > PROCS_MAX) {
err("%d processes is too many: max = %d\n", num_procs, PROCS_MAX);
exit(1);
}
if (pipe(pipes)) {
err("Can't create pipes: %m\n");
exit(1);
}
if (signal(SIGCHLD, inc_num_exited) == SIG_ERR) {
err("can't set SIGCHLD handler: %m\n");
exit(1);
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
pid = test_fork();
if (pid < 0) {
err("Can't fork: %m\n");
kill(0, SIGKILL);
exit(1);
}
if (pid == 0) {
close(pipes[1]);
while (test_go()) {
int rlen = read(pipes[0], buf, sizeof(buf));
if (rlen == 0)
break;
else if (rlen < 0) {
ret = errno; /* pass errno as exit code to the parent */
break;
}
for (i = 0; i < rlen && buf[i] == SND_CHR; i++)
;
if (i < rlen) {
ret = EILSEQ;
break;
}
}
test_waitsig(); /* even if failed, wait for migration to complete */
close(pipes[0]);
exit(ret);
}
}
close(pipes[0]);
if (num_exited) {
err("Some children died unexpectedly\n");
kill(0, SIGKILL);
exit(1);
}
test_daemon();
memset(buf, SND_CHR, sizeof(buf));
while(test_go())
if (write(pipes[1], buf, sizeof(buf)) < 0 &&
(errno != EINTR || test_go())) { /* only SIGTERM may stop us */
fail("write failed: %m\n");
ret = 1;
break;
}
close(pipes[1]);
test_waitsig(); /* even if failed, wait for migration to complete */
if (kill(0, SIGTERM)) {
fail("failed to send SIGTERM to my process group: %m\n");
goto out; /* shouldn't wait() in this case */
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
int chret;
if (wait(&chret) < 0) {
fail("can't wait for a child: %m\n");
ret = 1;
continue;
}
chret = WEXITSTATUS(chret);
if (chret) {
fail("child exited with non-zero code %d (%s)\n",
chret, strerror(chret));
ret = 1;
continue;
}
}
if (!ret)
pass();
out:
return 0;
}
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include "zdtmtst.h"
const char *test_doc = "Multi-process socket loop";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#define PROCS_DEF 4
#define PROCS_MAX 64
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes to create "
"(default " __stringify(PROCS_DEF)
", max " __stringify(PROCS_MAX) ")", 0);
volatile sig_atomic_t num_exited = 0;
void inc_num_exited(int signo)
{
num_exited++;
}
int main(int argc, char **argv)
{
int ret = 0;
pid_t pid;
int i;
uint8_t buf[0x100000];
int socks[PROCS_MAX * 2];
int in, out;
test_init(argc, argv);
if (num_procs > PROCS_MAX) {
err("%d processes is too many: max = %d\n", num_procs, PROCS_MAX);
exit(1);
}
for (i = 0; i < num_procs; i++)
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, socks + i * 2)) {
err("Can't create socks: %m\n");
exit(1);
}
if (signal(SIGCHLD, inc_num_exited) == SIG_ERR) {
err("can't set SIGCHLD handler: %m\n");
exit(1);
}
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
pid = test_fork();
if (pid < 0) {
err("Can't fork: %m\n");
kill(0, SIGKILL);
exit(1);
}
if (pid == 0) {
int j;
in = i * 2;
out = in - 1;
for (j = 0; j < num_procs * 2; j++)
if (j != in && j != out)
close(socks[j]);
signal(SIGPIPE, SIG_IGN);
if (pipe_in2out(socks[in], socks[out], buf, sizeof(buf)) < 0)
/* pass errno as exit code to the parent */
if (test_go() /* signal NOT delivered */ ||
(errno != EINTR && errno != EPIPE
&& errno != ECONNRESET))
ret = errno;
test_waitsig(); /* even if failed, wait for migration to complete */
close(socks[in]);
close(socks[out]);
exit(ret);
}
}
for (i = 1; i < num_procs * 2 - 1; i++)
close(socks[i]);
in = socks[0];
out = socks[num_procs * 2 - 1];
/* don't block on writing, _do_ block on reading */
if (set_nonblock(out,1) < 0) {
err("setting O_NONBLOCK failed: %m");
exit(1);
}
if (num_exited) {
err("Some children died unexpectedly\n");
kill(0, SIGKILL);
exit(1);
}
test_daemon();
while (test_go()) {
int len, rlen = 0, wlen;
uint8_t rbuf[sizeof(buf)], *p;
datagen(buf, sizeof(buf), NULL);
wlen = write(out, buf, sizeof(buf));
if (wlen < 0) {
if (errno == EINTR)
continue;
else {
fail("write failed: %m\n", i);
ret = 1;
break;
}
}
for (p = rbuf, len = wlen; len > 0; p += rlen, len -= rlen) {
rlen = read(in, p, len);
if (rlen <= 0)
break;
}
if (rlen < 0 && errno == EINTR)
continue;
if (len > 0) {
fail("read failed: %m\n");
ret = 1;
break;
}
if (memcmp(buf, rbuf, wlen)) {
fail("data mismatch\n");
ret = 1;
break;
}
}
test_waitsig(); /* even if failed, wait for migration to complete */
/* We expect that write(2) in child may return error only after signal
* has been received. Thus, send signal before closing parent fds.
*/
if (kill(0, SIGTERM)) {
fail("failed to send SIGTERM to my process group: %m\n");
goto out; /* shouldn't wait() in this case */
}
if (close(out))
fail("Failed to close parent fd 'out': %m\n");
/* If last child in the chain (from whom we read data) receives signal
* after parent has finished reading but before calling write(2), this
* child can block forever. To avoid this, close 'in' fd.
*/
if (close(in))
fail("failed to close parent fd 'in': %m\n");
for (i = 1; i < num_procs; i++) { /* i = 0 - parent */
int chret;
if (wait(&chret) < 0) {
fail("can't wait for a child: %m\n");
ret = 1;
continue;
}
chret = WEXITSTATUS(chret);
if (chret) {
fail("child %d exited with non-zero code %d (%s)\n",
i, chret, strerror(chret));
ret = 1;
continue;
}
}
if (!ret)
pass();
out:
return 0;
}
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Multi-client - server app";
const char *test_author = "Roman Kagan <rkagan@parallels.com>";
#define PROCS_DEF 4
#define PROCS_MAX 64
unsigned int num_procs = PROCS_DEF;
TEST_OPTION(num_procs, uint, "# processes to create "
"(default " __stringify(PROCS_DEF)
", max " __stringify(PROCS_MAX) ")", 0);
char *filename;
TEST_OPTION(filename, string, "file name", 1);
#define ACCEPT_TIMEOUT 100 /* max delay for the child to connect */
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 (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
err("can't make socket \"%s\" non-blocking: %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 accept_one_conn(int sock)
{
int acc_sock;
fd_set fds;
struct timeval timeout = {
.tv_sec = ACCEPT_TIMEOUT,
};
FD_ZERO(&fds);
FD_SET(sock, &fds);
switch (select(FD_SETSIZE, &fds, NULL, NULL, &timeout)) {
case 1:
break;
case 0:
err("timeout accepting a connection");
return -1;
default:
err("error while waiting for a connection: %m");
return -1;
}
acc_sock = accept(sock, NULL, NULL);
if (acc_sock < 0)
err("error accepting a connection: %m");
return acc_sock;
}
static int setup_clnt_sock(void)
{
struct sockaddr_un name;
int sock;
int ret = 0;
if (fill_sock_name(&name, filename) < 0) {
ret = -errno;
err("filename \"%s\" is too long", filename);
return ret;
}
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
ret = -errno;
err("can't create socket: %m");
return ret;
}
if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
ret = -errno;
err("can't connect: %m");
goto err;
}
return sock;
err:
close(sock);
return ret;
}
#define BUFLEN 1000
static int child(void)
{
int ret = 1;
uint8_t buf[BUFLEN];
uint32_t crc = ~0;
int sock = setup_clnt_sock();
if (sock < 0) {
ret = -sock;
goto out;
}
signal(SIGPIPE, SIG_IGN);
while (test_go()) {
datagen(buf, sizeof(buf), &crc);
if (write(sock, buf, sizeof(buf)) < 0 &&
(test_go() /* signal NOT received */ ||
(errno != EINTR && errno != EPIPE && \
errno != ECONNRESET))) {
ret = errno;
fail("child write: %m\n");
goto out;
}
}
ret = 0;
out:
close(sock);
return ret;
}
int main(int argc, char **argv)
{
struct {
pid_t pid;
int sock;
uint32_t crc;
} child_desc[PROCS_MAX];
int i, nproc;
int sock;
uint8_t buf[BUFLEN];
fd_set active_fds, read_fds;
test_init(argc, argv);
if (num_procs > PROCS_MAX) {
err("%d processes is too many: max = %d\n", num_procs, PROCS_MAX);
exit(1);
}
sock = setup_srv_sock();
if (sock < 0)
exit(1);
FD_ZERO(&active_fds);
for (nproc = 0; nproc < num_procs; nproc++) {
child_desc[nproc].pid = test_fork();
if (child_desc[nproc].pid < 0) {
err("can't fork: %m");
goto cleanup;
}
if (child_desc[nproc].pid == 0) {
close(sock);
exit(child());
}
child_desc[nproc].sock = accept_one_conn(sock);
if (child_desc[nproc].sock < 0) {
kill(child_desc[nproc].pid, SIGKILL);
goto cleanup;
}
child_desc[nproc].crc = ~0;
FD_SET(child_desc[nproc].sock, &active_fds);
}
close(sock); /* no more connections */
test_daemon();
while (test_go()) {
read_fds = active_fds;
if (select(FD_SETSIZE, &read_fds, NULL, NULL, NULL) < 0 &&
errno != EINTR) {
fail("error waiting for data: %m");
goto out;
}
for (i = 0; i < num_procs; i++)
if (FD_ISSET(child_desc[i].sock, &read_fds)) {
if (read(child_desc[i].sock, buf, sizeof(buf)) < 0) {
if(errno == EINTR) /* we're asked to stop */
break;
else {
fail("error reading data from socket: %m");
goto out;
}
}
if (datachk(buf, sizeof(buf), &child_desc[i].crc)) {
fail("CRC mismatch");
goto out;
}
}
}
out:
test_waitsig();
if (kill(0, SIGTERM)) {
fail("failed to send SIGTERM to my process group: %m\n");
goto cleanup; /* shouldn't wait() in this case */
}
while (nproc-- > 0) {
int chret;
/*
* Close socket to make sure that child's write() returns.
* This is to avoid race when server stopped reading & sent
* signal to child, child has checked for signal & found none
* (not yet delivered), then called write(), blocking forever.
*/
if(close(child_desc[nproc].sock))
fail("Can't close server socket: %m\n");
if (wait(&chret) < 0) {
fail("can't wait for a child: %m\n");
goto cleanup;
}
chret = WEXITSTATUS(chret);
if (chret) {
fail("child exited with non-zero code %d (%s)\n",
chret, strerror(chret));
goto cleanup;
}
}
pass();
cleanup:
while (nproc-- > 0) {
close(child_desc[nproc].sock);
if (child_desc[nproc].pid > 0)
kill(child_desc[nproc].pid, SIGKILL);
}
close(sock);
unlink(filename);
return 0;
}
LIBDIR = ../../lib
LIB = $(LIBDIR)/libzdtmtst.a
override CPPFLAGS += -I$(LIBDIR)
CFLAGS = -g -O2 -Wall
TST_NOFILE = \
file_read \
ipc \
ptrace \
epoll \
TST_FILE = \
TST = $(TST_NOFILE) $(TST_FILE)
SRC = $(TST:%=%.c)
OBJ = $(SRC:%.c=%.o)
DEP = $(SRC:%.c=%.d)
PID = $(TST:%=%.pid)
OUT = $(TST:%=%.out)
DEPEND.c = $(COMPILE.c) -MM -MP
%.d: %.c
$(DEPEND.c) $(OUTPUT_OPTION) $<
all: $(TST)
install: all
$(TST_NOFILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out
$(TST_FILE:%=%.pid): %.pid: %
$(<D)/$(<F) --pidfile=$@ --outfile=$<.out --filename=$<.test
%.out: %.pid %
-kill -TERM `< $<`
start: $(PID)
%.is_running: %.pid
kill -0 `< $<`
check_start: $(PID:%.pid=%.is_running)
stop:
-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)
ptrace.o: override CFLAGS += -pthread
ptrace: override LDFLAGS += -pthread
%: %.sh
cp $< $@
chmod +x $@
$(LIB): force
$(MAKE) -C $(LIBDIR)
clean:
$(RM) $(OBJ) $(TST) *~
cleandep: clean
$(RM) $(DEP)
cleanout:
$(RM) *.pid *.out* *.test chew*
realclean: cleandep cleanout
.PHONY: force clean cleandep cleanout realclean start check_start stop wait_stop
-include $(DEP)
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include <sys/epoll.h>
#include "zdtmtst.h"
const char *test_doc = "migrate application using epoll";
#define MAX_SCALE 128
enum child_exit_codes {
SUCCESS = 0,
GETTIMEOFDAYERROR,
WRITEERROR,
MAX_EXIT_CODE
};
static char *child_fail_reason[] = {
"Success",
"Can't get time",
"Can't write"
};
int scale = 13;
TEST_OPTION(scale, int, "How many children should perform testing", 0);
static int pids[MAX_SCALE];
static int fds[MAX_SCALE][2];
static volatile int stop = 0;
static void killall(void)
{
int i;
for (i = 0; i < scale; i++) {
close(fds[i][0]);
close(fds[i][1]);
kill(pids[i], SIGUSR2);
}
}
static void do_stop(int sig)
{
stop = 1;
}
static void run_child(int num)
{
int fd = fds[num][1];
uint32_t crc = ~0;
size_t buf_size=512;
char buf[buf_size];
struct timeval tv;
struct timespec ts;
int rv;
close(fds[num][0]);
datagen(buf, sizeof(buf), &crc);
if (gettimeofday(&tv, NULL) < 0) {
rv = GETTIMEOFDAYERROR;
goto out;
}
srand(tv.tv_sec + tv.tv_usec);
ts.tv_sec = 0;
while (!stop) {
ts.tv_nsec = rand() % 999999999;
nanosleep(&ts, &ts);
if (write(fd, buf, buf_size) < 0 &&
(!stop /* signal SIGUSR2 NOT received */ ||
(errno != EINTR && errno != EPIPE))) {
fail("child write: %m\n");
rv = WRITEERROR;
goto out;
}
}
rv = SUCCESS;
out: close(fds[num][1]);
exit(rv);
}
int main(int argc, char **argv)
{
int rv, i;
int counter = 0;
int efd;
size_t buf_size=512;
char buf[buf_size];
struct epoll_event event = {
.events = EPOLLIN
}, *events;
test_init(argc, argv);
if (scale > MAX_SCALE) {
err("Too many children specified\n");
exit(1);
}
if (signal(SIGUSR2, do_stop) == SIG_ERR) {
err("Can't setup handler\n");
exit(1);
}
if ((efd = epoll_create(scale)) < 0) {
err("Can't create epoll: %m\n");
exit(1);
}
for (i = 0; i < scale; i++) {
if (pipe(fds[i]) < 0) {
err("Can't create pipe[%d]: %m\n", i);
killall();
exit(1);
}
if (fcntl(fds[i][0], F_SETFL, O_NONBLOCK) < 0) {
err("Can't set O_NONBLOCK flag on fd[%d]: %m\n", i);
killall();
exit(1);
}
event.data.fd = fds[i][0];
if (epoll_ctl(efd, EPOLL_CTL_ADD, fds[i][0], &event) < 0) {
err("Can't add fd[%d]: %m\n", i);
killall();
exit(1);
}
if ((rv = test_fork()) < 0) {
err("Can't fork[%d]: %m\n", i);
killall();
exit(1);
}
if (rv == 0)
run_child(i);
close(fds[i][1]);
pids[i] = rv;
}
if ((events = (struct epoll_event*) malloc (sizeof(struct epoll_event)*scale)) == NULL) {
err("Can't allocate memory: %m\n");
killall();
exit(1);
}
test_daemon();
while (test_go()) {
if ((rv = epoll_wait(efd, events, scale, rand() % 999)) < 0 && errno != EINTR) {
err("epoll_wait error: %m\n");
killall();
exit(1);
}
for (i = 0; i < rv; i++) {
while (read(events[i].data.fd, buf, buf_size) > 0);
if (errno != EAGAIN && errno != 0 && errno) {
err("read error: %m\n");
killall();
exit(1);
}
}
}
test_waitsig();
killall();
for (i = 0; i < scale; i++) {
if (waitpid(pids[i], &rv, 0) < 0) {
fail("waitpid error: %m\n");
counter++;
continue;
}
else {
rv = WEXITSTATUS(rv);
if (rv < MAX_EXIT_CODE && rv > SUCCESS) {
fail("Child failed: %s (%d)\n",
child_fail_reason[rv], rv);
counter++;
} else if (rv != SUCCESS) {
fail("Unknow exitcode from child: %d\n", rv);
counter++;
}
}
}
if (counter == 0)
pass();
return 0;
}
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include "zdtmtst.h"
const char *test_doc = "Fill/read file continuously to check"
"it's migrated at the right moment";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#define MAX_SCALE 128
#define FILE_SIZE (16 * 1024)
enum kids_exit_codes {
SUCCESS = 0,
FILE_CORRUPTED,
MMAP_FAILED,
OPEN_FAILED,
WRITE_FAILED,
READ_FAILED,
FSYNC_FAILED,
SEEK_FAILED,
MAX_EXIT_CODE_VAL
};
static char *kids_fail_reasons[] = {
"Success",
/* 1 */ "File corrupted",
/* 2 */ "Map failed",
/* 3 */ "Open (create) failed",
/* 4 */ "Write failed",
/* 5 */ "Read failed",
/* 6 */ "Fsync failed",
/* 7 */ "Lseek failed"
};
int scale = 13;
TEST_OPTION(scale, int, "How many children should perform testing", 0);
static int pids[MAX_SCALE];
static volatile int stop = 0;
static void killall(void)
{
int i;
for (i = 0; i < MAX_SCALE; i++)
kill(pids[i], SIGUSR2);
}
static void do_stop(int sig)
{
stop = 1;
}
static char *buf;
static void prepare_buf(void)
{
int i;
for (i = 0; i < FILE_SIZE; i++)
buf[i] = rand();
}
static int fill_file(int fd)
{
int rv, wr;
if (lseek(fd, 0, SEEK_SET) == -1)
return -2;
wr = 0;
while (1) {
rv = write(fd, buf + wr, FILE_SIZE - wr);
if (rv <= 0)
return -1;
wr += rv;
if (wr == FILE_SIZE)
break;
}
return 0;
}
static int check_file(int fd)
{
char rbuf[1024];
int rv, rd;
if (lseek(fd, 0, SEEK_SET) == -1)
return -2;
rd = 0;
while (1) {
rv = read(fd, rbuf, 1024);
if (rv <= 0)
return -1;
if (memcmp(buf + rd, rbuf, rv))
return 1;
rd += rv;
if (rd == FILE_SIZE)
break;
}
return 0;
}
static void chew_some_file(int num)
{
char filename[10];
int fd, rv;
buf = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, 0, 0);
rv = MMAP_FAILED;
if (buf == MAP_FAILED)
goto out_exit;
sprintf(filename, "chew%d", num);
fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0666);
rv = OPEN_FAILED;
if (fd == -1)
goto out_unmap;
while (!stop) {
prepare_buf();
switch (fill_file(fd)) {
case -1:
rv = WRITE_FAILED;
goto out_exit;
case -2:
rv = SEEK_FAILED;
goto out_exit;
}
if (fsync(fd) == -1) {
rv = FSYNC_FAILED;
goto out_exit;
}
if (fsync(fd) == -1) {
rv = FSYNC_FAILED;
goto out_exit;
}
switch (check_file(fd)) {
case -1:
rv = READ_FAILED;
goto out_exit;
case -2:
rv = SEEK_FAILED;
goto out_exit;
case 1:
rv = FILE_CORRUPTED;
int fd1;
char str[32];
// create standard file
sprintf(str, "standard_%s", filename);
fd1 = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (write(fd1, buf, FILE_SIZE) != FILE_SIZE)
err("can't write %s: %m\n", str);
close(fd1);
goto out_exit;
}
}
rv = SUCCESS;
close(fd);
unlink(filename);
out_unmap:
munmap(buf, FILE_SIZE);
out_exit:
exit(rv);
}
int main(int argc, char **argv)
{
int rv, i;
int counter = 0;
test_init(argc, argv);
if (scale > MAX_SCALE) {
err("Too many children specified\n");
exit(-1);
}
if (signal(SIGUSR2, do_stop) == SIG_ERR) {
err("Can't setup handler\n");
exit(-1);
}
for (i = 0; i < scale; i++) {
rv = test_fork();
if (rv == -1) {
err("Can't fork\n");
killall();
exit(-1);
}
if (rv == 0)
chew_some_file(i);
pids[i] = rv;
}
test_daemon();
test_waitsig();
killall();
for (i = 0; i < scale; i++) {
if (waitpid(pids[i], &rv, 0) == -1) {
fail("Can't wipe up the kid\n");
counter++;
continue;
}
if (!WIFEXITED(rv)) {
fail("Kid was killed\n");
counter++;
} else {
rv = WEXITSTATUS(rv);
if (rv < MAX_EXIT_CODE_VAL && rv > SUCCESS) {
fail("Kid failed: %s (%d)\n",
kids_fail_reasons[rv], rv);
counter++;
} else if (rv != SUCCESS) {
fail("Unknow exitcode from kid: %d\n", rv);
counter++;
}
}
}
if (counter == 0)
pass();
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/shm.h>
#include <signal.h>
#include <errno.h>
#include "zdtmtst.h"
const char *test_doc="Tests ipc sems and shmems migrate fine";
const char *test_author="Pavel Emelianov <xemul@parallels.com>";
static struct sembuf unlock = {
.sem_op = 1,
.sem_num = 0,
.sem_flg = 0,
};
static struct sembuf lock = {
.sem_op = -1,
.sem_num = 0,
.sem_flg = 0,
};
#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)
#define POISON 0xac
static inline void poison_area(int *mem)
{
memset(mem, POISON, shmem_size);
}
static int child(key_t key)
{
int sem, shm, ret, res = 0;
uint8_t *mem;
uint32_t crc;
sem = semget(key, 1, 0777);
if (sem == -1)
return -1;
shm = shmget(key, shmem_size, 0777);
if (shm == -1)
return -2;
mem = shmat(shm, NULL, 0);
if (mem == (uint8_t *)-1)
return -3;
while (test_go()) {
ret = semop(sem, &lock, 1);
if (ret) {
if (errno == EINTR)
continue;
fail("Error in semop lock");
res = errno;
break;
}
crc = INIT_CRC;
datagen(mem, shmem_size, &crc);
while ((ret = semop(sem, &unlock, 1)) && (errno == EINTR));
if (ret) {
fail("Error in semop unlock");
res = errno;
break;
}
}
shmdt(mem);
return res;
}
int main(int argc, char **argv)
{
key_t key;
int sem, shm, pid1, pid2;
int fail_count = 0;
uint8_t *mem;
uint32_t crc;
int ret;
test_init(argc, argv);
key = ftok(argv[0], 822155650);
if (key == -1) {
err("Can't make key");
goto out;
}
sem = semget(key, 1, 0777 | IPC_CREAT | IPC_EXCL);
if (sem == -1) {
err("Can't get sem");
goto out;
}
if (semctl(sem, 0, SETVAL, 1) == -1) {
err("Can't init sem");
fail_count++;
goto out_sem;
}
shm = shmget(key, shmem_size, 0777 | IPC_CREAT | IPC_EXCL);
if (shm == -1) {
err("Can't get shm");
fail_count++;
goto out_sem;
}
mem = shmat(shm, NULL, 0);
if (mem == (void *)-1) {
err("Can't attach shm");
fail_count++;
goto out_shm;
}
poison_area((int *)mem);
pid1 = test_fork();
if (pid1 == -1) {
err("Can't fork 1st time");
goto out_shdt;
} else if (pid1 == 0)
exit(child(key));
pid2 = test_fork();
if (pid2 == -1) {
err("Can't fork 2nd time");
fail_count++;
goto out_child;
} else if (pid2 == 0)
exit(child(key));
test_daemon();
while (test_go()) {
ret = semop(sem, &lock, 1);
if (ret) {
if (errno == EINTR)
continue;
fail_count++;
fail("Error in semop lock");
break;
}
if (mem[0] != POISON) {
crc = INIT_CRC;
if (datachk(mem, shmem_size, &crc)) {
fail_count++;
fail("Semaphore protection is broken or "
"shmem pages are messed");
semop(sem, &unlock, 1);
break;
}
poison_area((int *)mem);
}
while ((ret = semop(sem, &unlock, 1)) && (errno == EINTR));
if (ret) {
fail_count++;
fail("Error in semop unlock");
break;
}
}
test_waitsig();
kill(pid2, SIGTERM);
waitpid(pid2, &ret, 0);
if (!WIFEXITED(ret)) {
fail_count++;
err("Child 2 was killed");
} else if (WEXITSTATUS(ret)) {
fail_count++;
err("Child 2 couldn't inititalise");
}
out_child:
kill(pid1, SIGTERM);
waitpid(pid1, &ret, 0);
if (!WIFEXITED(ret)) {
fail_count++;
err("Child 1 was killed");
} else if (WEXITSTATUS(ret)) {
fail_count++;
err("Child 1 couldn't inititalise");
}
out_shdt:
shmdt(mem);
out_shm:
shmctl(shm, IPC_RMID, NULL);
out_sem:
semctl(sem, 1, IPC_RMID);
if (fail_count == 0)
pass();
out:
return 0;
}
#include <stdio.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include "zdtmtst.h"
const char *test_doc = "Tests that ptraced thread do not escape from tracing";
const char *test_author = "Pavel Emelianov <xemul@parallels.com>";
#define NR_THREADS 2
unsigned int nr_threads = NR_THREADS;
TEST_OPTION(nr_threads, uint, "Number of threads", 0);
static void *thread(void *arg)
{
*(int *)arg = syscall(SYS_gettid);
while (1)
sleep(1);
}
int main(int argc, char **argv)
{
int pid, status, i, stopped;
#define PT_REGS_SIZE 4096 /* big enough for any arch */
#define PT_REGS_ALIGN 16 /* big enough for any arch */
char regs[PT_REGS_SIZE] __attribute__((aligned(PT_REGS_ALIGN)));
int *pids;
test_init(argc, argv);
pids = (int *)mmap(NULL, sizeof(int) * nr_threads,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON, 0, 0);
if (pids == MAP_FAILED) {
err("Can't map");
exit(1);
}
memset(pids, 0, sizeof(int) * nr_threads);
pid = fork();
if (pid < 0) {
err("Can't fork");
goto out;
} else if (pid == 0) {
pthread_t pt[nr_threads];
for (i = 0; i < nr_threads - 1; i++) {
if (pthread_create(&pt[i], NULL, thread, pids + i)) {
err("Can't make thread");
goto out_th;
}
}
thread(pids + i);
out_th:
for (i--; i >=0; i--) {
pthread_kill(pt[i], SIGKILL);
pthread_join(pt[i], NULL);
}
return 0;
}
for (i = 0; i < nr_threads; i++) {
while (pids[i] == 0)
sched_yield();
if (ptrace(PTRACE_ATTACH, pids[i], (char *)1, NULL) == -1) {
err("Can't attach");
goto out_pt;
}
}
test_daemon();
while (test_go()) {
for (i = 0; i < nr_threads; i++)
if (pids[i])
break;
if (i == nr_threads)
break;
stopped = wait4(-1, &status, __WALL, NULL);
if (stopped == -1) {
err("Can't wait");
break;
}
if (WIFSTOPPED(status)) {
if (ptrace(PTRACE_GETREGS, stopped, NULL, regs)) {
/* FAIL */
fail("Ptrace won't work");
break;
}
for (i = 0; i < nr_threads; i++)
if (pids[i] == stopped)
break;
if (i == nr_threads)
continue;
pids[i] = 0;
ptrace(PTRACE_DETACH, stopped, (char *)1, NULL);
ptrace(PTRACE_CONT, stopped, (char *)1, NULL);
continue;
}
}
test_waitsig();
pass();
out_pt:
kill(pid, SIGKILL);
wait(NULL);
out:
munmap(pids, sizeof(int) * nr_threads);
return 0;
}
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