Merge branch 'spring-merge' of ssh://git.qubes-os.org/var/lib/qubes/git/rafal/core into spring-merge
This commit is contained in:
commit
7f94cf2709
2
Makefile
2
Makefile
@ -33,3 +33,5 @@ clean:
|
||||
(cd dom0/restore && make clean)
|
||||
(cd dom0/qmemman && make clean)
|
||||
(cd common && make clean)
|
||||
make -C qrexec clean
|
||||
make -C vchan clean
|
||||
|
@ -1,6 +1,14 @@
|
||||
CC=gcc
|
||||
CFLAGS=-Wall
|
||||
all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm
|
||||
CFLAGS=-g -Wall -I../common
|
||||
all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm dvm_file_editor qfile-agent-dvm qfile-agent qfile-unpacker
|
||||
dvm_file_editor: dvm_file_editor.o ../common/ioall.o
|
||||
$(CC) -g -o dvm_file_editor dvm_file_editor.o ../common/ioall.o
|
||||
qfile-agent-dvm: qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o
|
||||
$(CC) -g -o qfile-agent-dvm qfile-agent-dvm.o ../common/ioall.o ../common/gui-fatal.o
|
||||
qfile-agent: qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o
|
||||
$(CC) -g -o qfile-agent qfile-agent.o ../common/ioall.o ../common/gui-fatal.o copy_file.o
|
||||
qfile-unpacker: qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o
|
||||
$(CC) -g -o qfile-unpacker qfile-unpacker.o ../common/ioall.o ../common/gui-fatal.o copy_file.o unpack.o
|
||||
qubes_penctl: qubes_penctl.o
|
||||
$(CC) -o qubes_penctl qubes_penctl.o -lxenstore
|
||||
qubes_add_pendrive_script: qubes_add_pendrive_script.o
|
||||
@ -9,4 +17,5 @@ qvm-open-in-dvm: qvm-open-in-dvm.o
|
||||
$(CC) -o qvm-open-in-dvm qvm-open-in-dvm.o -lxenstore
|
||||
|
||||
clean:
|
||||
rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch *.o *~
|
||||
rm -f qfile-agent-dvm qfile-agent qfile-unpacker dvm_file_editor qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~
|
||||
rm -f xenstore-watch
|
||||
|
28
appvm/copy_file.c
Normal file
28
appvm/copy_file.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include <unistd.h>
|
||||
#include <ioall.h>
|
||||
extern void notify_progress(int, int);
|
||||
|
||||
char * copy_file(int outfd, int infd, long long size)
|
||||
{
|
||||
char buf[4096];
|
||||
long long written = 0;
|
||||
int ret;
|
||||
int count;
|
||||
while (written < size) {
|
||||
if (size - written > sizeof(buf))
|
||||
count = sizeof buf;
|
||||
else
|
||||
count = size - written;
|
||||
ret = read(infd, buf, count);
|
||||
if (!ret)
|
||||
return("EOF while reading file");
|
||||
if (ret < 0)
|
||||
return("error reading file");
|
||||
if (!write_all(outfd, buf, ret))
|
||||
return("error writing file content");
|
||||
notify_progress(ret, 0);
|
||||
written += ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
2
appvm/dvm2.h
Normal file
2
appvm/dvm2.h
Normal file
@ -0,0 +1,2 @@
|
||||
#define DVM_FILENAME_SIZE 256
|
||||
#define DVM_SPOOL "/home/user/.dvmspool"
|
73
appvm/dvm_file_editor.c
Normal file
73
appvm/dvm_file_editor.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <ioall.h>
|
||||
#include "dvm2.h"
|
||||
|
||||
char *get_filename()
|
||||
{
|
||||
char buf[DVM_FILENAME_SIZE];
|
||||
static char retname[sizeof(buf) + sizeof("/tmp/")];
|
||||
if (!read_all(0, buf, sizeof(buf)))
|
||||
exit(1);
|
||||
if (index(buf, '/')) {
|
||||
fprintf(stderr, "filename contains /");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(retname, sizeof(retname), "/tmp/%s", buf);
|
||||
return retname;
|
||||
}
|
||||
|
||||
void copy_file(char *filename)
|
||||
{
|
||||
int fd = open(filename, O_WRONLY | O_CREAT, 0600);
|
||||
if (fd < 0) {
|
||||
perror("open file");
|
||||
exit(1);
|
||||
}
|
||||
if (!copy_fd_all(fd, 0))
|
||||
exit(1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void send_file_back(char * filename)
|
||||
{
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open file");
|
||||
exit(1);
|
||||
}
|
||||
if (!copy_fd_all(1, fd))
|
||||
exit(1);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
char cmdbuf[512];
|
||||
struct stat stat_pre, stat_post;
|
||||
char *filename = get_filename();
|
||||
|
||||
copy_file(filename);
|
||||
if (stat(filename, &stat_pre)) {
|
||||
perror("stat pre");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(cmdbuf, sizeof(cmdbuf),
|
||||
"HOME=/home/user DISPLAY=:0 /usr/bin/mimeopen -n -M '%s' 2>&1 > /tmp/kde-open.log </dev/null",
|
||||
filename);
|
||||
if (system(cmdbuf))
|
||||
system
|
||||
("HOME=/home/user DISPLAY=:0 /usr/bin/kdialog --sorry 'Unable to handle mimetype of the requested file!'");
|
||||
if (stat(filename, &stat_post)) {
|
||||
perror("stat post");
|
||||
exit(1);
|
||||
}
|
||||
if (stat_pre.st_mtime != stat_post.st_mtime)
|
||||
send_file_back(filename);
|
||||
return 0;
|
||||
}
|
18
appvm/filecopy.h
Normal file
18
appvm/filecopy.h
Normal file
@ -0,0 +1,18 @@
|
||||
#define FILECOPY_SPOOL "/home/user/.filecopyspool"
|
||||
#define FILECOPY_VMNAME_SIZE 32
|
||||
#define PROGRESS_NOTIFY_DELTA (15*1000*1000)
|
||||
#define MAX_PATH_LENGTH 16384
|
||||
|
||||
#define LEGAL_EOF 31415926
|
||||
|
||||
struct file_header {
|
||||
unsigned int namelen;
|
||||
unsigned int mode;
|
||||
unsigned long long filelen;
|
||||
unsigned int atime;
|
||||
unsigned int atime_nsec;
|
||||
unsigned int mtime;
|
||||
unsigned int mtime_nsec;
|
||||
};
|
||||
|
||||
char * copy_file(int outfd, int infd, long long size);
|
139
appvm/qfile-agent-dvm.c
Normal file
139
appvm/qfile-agent-dvm.c
Normal file
@ -0,0 +1,139 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <ioall.h>
|
||||
#include <unistd.h>
|
||||
#include <gui-fatal.h>
|
||||
#include "dvm2.h"
|
||||
|
||||
void send_file(char *fname)
|
||||
{
|
||||
char *base;
|
||||
int fd = open(fname, O_RDONLY);
|
||||
if (fd < 0)
|
||||
gui_fatal("open %s", fname);
|
||||
base = rindex(fname, '/');
|
||||
if (!base)
|
||||
base = fname;
|
||||
else
|
||||
base++;
|
||||
if (strlen(base) >= DVM_FILENAME_SIZE)
|
||||
base += strlen(base) - DVM_FILENAME_SIZE + 1;
|
||||
if (!write_all(1, base, DVM_FILENAME_SIZE))
|
||||
gui_fatal("send filename to dispVM");
|
||||
if (!copy_fd_all(1, fd))
|
||||
gui_fatal("send file to dispVM");
|
||||
close(1);
|
||||
}
|
||||
|
||||
int copy_and_return_nonemptiness(int tmpfd)
|
||||
{
|
||||
struct stat st;
|
||||
if (!copy_fd_all(tmpfd, 0))
|
||||
gui_fatal("receiving file from dispVM");
|
||||
if (fstat(tmpfd, &st))
|
||||
gui_fatal("fstat");
|
||||
close(tmpfd);
|
||||
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
void recv_file_nowrite(char *fname)
|
||||
{
|
||||
char *tempfile;
|
||||
char *errmsg;
|
||||
int tmpfd;
|
||||
|
||||
asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX");
|
||||
tmpfd = mkstemp(tempfile);
|
||||
if (tmpfd < 0)
|
||||
gui_fatal("unable to create any temporary file, aborting");
|
||||
if (!copy_and_return_nonemptiness(tmpfd)) {
|
||||
unlink(tempfile);
|
||||
return;
|
||||
}
|
||||
asprintf(&errmsg,
|
||||
"The file %s has been edited in Disposable VM and the modified content has been received, "
|
||||
"but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been "
|
||||
"saved to %s", fname, tempfile);
|
||||
gui_nonfatal(errmsg);
|
||||
}
|
||||
|
||||
void actually_recv_file(char *fname, char *tempfile, int tmpfd)
|
||||
{
|
||||
if (!copy_and_return_nonemptiness(tmpfd)) {
|
||||
unlink(tempfile);
|
||||
return;
|
||||
}
|
||||
if (rename(tempfile, fname))
|
||||
gui_fatal("rename");
|
||||
}
|
||||
|
||||
void recv_file(char *fname)
|
||||
{
|
||||
int tmpfd;
|
||||
char *tempfile;
|
||||
asprintf(&tempfile, "%s.XXXXXX", fname);
|
||||
tmpfd = mkstemp(tempfile);
|
||||
if (tmpfd < 0)
|
||||
recv_file_nowrite(fname);
|
||||
else
|
||||
actually_recv_file(fname, tempfile, tmpfd);
|
||||
}
|
||||
|
||||
void talk_to_daemon(char *fname)
|
||||
{
|
||||
send_file(fname);
|
||||
recv_file(fname);
|
||||
}
|
||||
|
||||
void process_spoolentry(char *entry_name)
|
||||
{
|
||||
char *abs_spool_entry_name;
|
||||
int entry_fd;
|
||||
struct stat st;
|
||||
char *filename;
|
||||
int entry_size;
|
||||
asprintf(&abs_spool_entry_name, "%s/%s", DVM_SPOOL, entry_name);
|
||||
entry_fd = open(abs_spool_entry_name, O_RDONLY);
|
||||
unlink(abs_spool_entry_name);
|
||||
if (entry_fd < 0 || fstat(entry_fd, &st))
|
||||
gui_fatal("bad dvm_entry");
|
||||
entry_size = st.st_size;
|
||||
filename = calloc(1, entry_size + DVM_FILENAME_SIZE);
|
||||
if (!filename)
|
||||
gui_fatal("malloc");
|
||||
if (!read_all(entry_fd, filename, entry_size))
|
||||
gui_fatal("read dvm entry %s", abs_spool_entry_name);
|
||||
close(entry_fd);
|
||||
talk_to_daemon(filename);
|
||||
}
|
||||
|
||||
void scan_spool(char *name)
|
||||
{
|
||||
struct dirent *ent;
|
||||
DIR *dir = opendir(name);
|
||||
if (!dir)
|
||||
gui_fatal("opendir %s", name);
|
||||
while ((ent = readdir(dir))) {
|
||||
char *fname = ent->d_name;
|
||||
if (!strcmp(fname, ".") || !strcmp(fname, ".."))
|
||||
continue;
|
||||
process_spoolentry(fname);
|
||||
break;
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
scan_spool(DVM_SPOOL);
|
||||
return 0;
|
||||
}
|
215
appvm/qfile-agent.c
Normal file
215
appvm/qfile-agent.c
Normal file
@ -0,0 +1,215 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <ioall.h>
|
||||
#include <unistd.h>
|
||||
#include <gui-fatal.h>
|
||||
#include "filecopy.h"
|
||||
|
||||
enum {
|
||||
PROGRESS_FLAG_NORMAL,
|
||||
PROGRESS_FLAG_INIT,
|
||||
PROGRESS_FLAG_DONE
|
||||
};
|
||||
|
||||
|
||||
|
||||
char *client_flags;
|
||||
void do_notify_progress(long long total, int flag)
|
||||
{
|
||||
FILE *progress;
|
||||
if (!client_flags[0])
|
||||
return;
|
||||
progress = fopen(client_flags, "w");
|
||||
if (!progress)
|
||||
return;
|
||||
fprintf(progress, "%d %lld %s", getpid(), total,
|
||||
flag == PROGRESS_FLAG_DONE ? "DONE" : "BUSY");
|
||||
fclose(progress);
|
||||
}
|
||||
|
||||
void notify_progress(int size, int flag)
|
||||
{
|
||||
static long long total = 0;
|
||||
static long long prev_total = 0;
|
||||
total += size;
|
||||
if (total > prev_total + PROGRESS_NOTIFY_DELTA
|
||||
|| (flag != PROGRESS_FLAG_NORMAL)) {
|
||||
do_notify_progress(total, flag);
|
||||
prev_total = total;
|
||||
}
|
||||
}
|
||||
|
||||
void write_headers(struct file_header *hdr, char *filename)
|
||||
{
|
||||
if (!write_all(1, hdr, sizeof(*hdr))
|
||||
|| !write_all(1, filename, hdr->namelen))
|
||||
gui_fatal("writing file headers to remove AppVM");
|
||||
}
|
||||
|
||||
int single_file_processor(char *filename, struct stat *st)
|
||||
{
|
||||
struct file_header hdr;
|
||||
int fd;
|
||||
mode_t mode = st->st_mode;
|
||||
|
||||
hdr.namelen = strlen(filename) + 1;
|
||||
hdr.mode = mode;
|
||||
hdr.atime = st->st_atim.tv_sec;
|
||||
hdr.atime_nsec = st->st_atim.tv_nsec;
|
||||
hdr.mtime = st->st_mtim.tv_sec;
|
||||
hdr.mtime_nsec = st->st_mtim.tv_nsec;
|
||||
|
||||
if (S_ISREG(mode)) {
|
||||
char *ret;
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (!fd)
|
||||
gui_fatal("open %s", filename);
|
||||
hdr.filelen = st->st_size;
|
||||
write_headers(&hdr, filename);
|
||||
ret = copy_file(1, fd, hdr.filelen);
|
||||
if (ret)
|
||||
gui_fatal("Copying file %s: %s", filename, ret);
|
||||
close(fd);
|
||||
}
|
||||
if (S_ISDIR(mode)) {
|
||||
hdr.filelen = 0;
|
||||
write_headers(&hdr, filename);
|
||||
}
|
||||
if (S_ISLNK(mode)) {
|
||||
char name[st->st_size + 1];
|
||||
if (readlink(filename, name, sizeof(name)) != st->st_size)
|
||||
gui_fatal("readlink %s", filename);
|
||||
hdr.filelen = st->st_size + 1;
|
||||
write_headers(&hdr, filename);
|
||||
if (!write_all(1, name, st->st_size + 1))
|
||||
gui_fatal("write to remote VM");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_fs_walk(char *file)
|
||||
{
|
||||
char *newfile;
|
||||
struct stat st;
|
||||
struct dirent *ent;
|
||||
DIR *dir;
|
||||
|
||||
if (lstat(file, &st))
|
||||
gui_fatal("stat %s", file);
|
||||
single_file_processor(file, &st);
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
return 0;
|
||||
dir = opendir(file);
|
||||
if (!dir)
|
||||
gui_fatal("opendir %s", file);
|
||||
while ((ent = readdir(dir))) {
|
||||
char *fname = ent->d_name;
|
||||
if (!strcmp(fname, ".") || !strcmp(fname, ".."))
|
||||
continue;
|
||||
asprintf(&newfile, "%s/%s", file, fname);
|
||||
do_fs_walk(newfile);
|
||||
free(newfile);
|
||||
}
|
||||
closedir(dir);
|
||||
// directory metadata is resent; this makes the code simple,
|
||||
// and the atime/mtime is set correctly at the second time
|
||||
single_file_processor(file, &st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_vmname(char *vmname)
|
||||
{
|
||||
char buf[FILECOPY_VMNAME_SIZE];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strncat(buf, vmname, sizeof(buf) - 1);
|
||||
if (!write_all(1, buf, sizeof buf))
|
||||
gui_fatal("writing vmname to remote VM");
|
||||
}
|
||||
|
||||
char *get_item(char *data, char **current, int size)
|
||||
{
|
||||
char *ret;
|
||||
if ((unsigned long) *current >= (unsigned long) data + size)
|
||||
return NULL;
|
||||
ret = *current;
|
||||
*current += strlen(ret) + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void parse_entry(char *data, int datasize)
|
||||
{
|
||||
char *current = data;
|
||||
char *vmname, *entry, *sep;
|
||||
vmname = get_item(data, ¤t, datasize);
|
||||
client_flags = get_item(data, ¤t, datasize);
|
||||
notify_progress(0, PROGRESS_FLAG_INIT);
|
||||
send_vmname(vmname);
|
||||
while ((entry = get_item(data, ¤t, datasize))) {
|
||||
do {
|
||||
sep = rindex(entry, '/');
|
||||
if (!sep)
|
||||
gui_fatal
|
||||
("Internal error: nonabsolute filenames not allowed");
|
||||
*sep = 0;
|
||||
} while (sep[1] == 0);
|
||||
if (entry[0] == 0)
|
||||
chdir("/");
|
||||
else if (chdir(entry))
|
||||
gui_fatal("chdir to %s", entry);
|
||||
do_fs_walk(sep + 1);
|
||||
}
|
||||
notify_progress(0, PROGRESS_FLAG_DONE);
|
||||
}
|
||||
|
||||
void process_spoolentry(char *entry_name)
|
||||
{
|
||||
char *abs_spool_entry_name;
|
||||
int entry_fd;
|
||||
struct stat st;
|
||||
char *entry;
|
||||
int entry_size;
|
||||
asprintf(&abs_spool_entry_name, "%s/%s", FILECOPY_SPOOL,
|
||||
entry_name);
|
||||
entry_fd = open(abs_spool_entry_name, O_RDONLY);
|
||||
unlink(abs_spool_entry_name);
|
||||
if (entry_fd < 0 || fstat(entry_fd, &st))
|
||||
gui_fatal("bad file copy spool entry");
|
||||
entry_size = st.st_size;
|
||||
entry = calloc(1, entry_size + 1);
|
||||
if (!entry)
|
||||
gui_fatal("malloc");
|
||||
if (!read_all(entry_fd, entry, entry_size))
|
||||
gui_fatal("read filecopy entry");
|
||||
close(entry_fd);
|
||||
parse_entry(entry, entry_size);
|
||||
}
|
||||
|
||||
void scan_spool(char *name)
|
||||
{
|
||||
struct dirent *ent;
|
||||
DIR *dir = opendir(name);
|
||||
if (!dir)
|
||||
gui_fatal("opendir %s", name);
|
||||
while ((ent = readdir(dir))) {
|
||||
char *fname = ent->d_name;
|
||||
if (fname[0] != '.') {
|
||||
process_spoolentry(fname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
scan_spool(FILECOPY_SPOOL);
|
||||
return 0;
|
||||
}
|
83
appvm/qfile-unpacker.c
Normal file
83
appvm/qfile-unpacker.c
Normal file
@ -0,0 +1,83 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <ioall.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/fsuid.h>
|
||||
#include <gui-fatal.h>
|
||||
#include <errno.h>
|
||||
#include "filecopy.h"
|
||||
#define INCOMING_DIR_ROOT "/home/user/incoming"
|
||||
int prepare_creds_return_uid(char *username)
|
||||
{
|
||||
struct passwd *pwd;
|
||||
pwd = getpwnam(username);
|
||||
if (!pwd) {
|
||||
perror("getpwnam");
|
||||
exit(1);
|
||||
}
|
||||
setenv("HOME", pwd->pw_dir, 1);
|
||||
setenv("USER", username, 1);
|
||||
setgid(pwd->pw_gid);
|
||||
initgroups(username, pwd->pw_gid);
|
||||
setfsuid(pwd->pw_uid);
|
||||
return pwd->pw_uid;
|
||||
}
|
||||
|
||||
void wait_for_child(int statusfd)
|
||||
{
|
||||
int status;
|
||||
if (read(statusfd, &status, sizeof status)!=sizeof status)
|
||||
gui_fatal("File copy error: Internal error reading status from unpacker");
|
||||
errno = status;
|
||||
switch (status) {
|
||||
case LEGAL_EOF: break;
|
||||
case 0: gui_fatal("File copy: Connection terminated unexpectedly"); break;
|
||||
case EINVAL: gui_fatal("File copy: Corrupted data from packer"); break;
|
||||
case EEXIST: gui_fatal("File copy: not overwriting existing file. Clean ~/incoming, and retry copy"); break;
|
||||
default: gui_fatal("File copy");
|
||||
}
|
||||
}
|
||||
|
||||
extern void do_unpack(int);
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
char *incoming_dir;
|
||||
int pipefds[2];
|
||||
int uid;
|
||||
|
||||
pipe(pipefds);
|
||||
|
||||
uid = prepare_creds_return_uid("user");
|
||||
|
||||
mkdir(INCOMING_DIR_ROOT, 0700);
|
||||
asprintf(&incoming_dir, "%s/from-%s", INCOMING_DIR_ROOT, argv[1]);
|
||||
mkdir(incoming_dir, 0700);
|
||||
if (chdir(incoming_dir))
|
||||
gui_fatal("Error chdir to %s", incoming_dir);
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
if (chroot(incoming_dir)) //impossible
|
||||
gui_fatal("Error chroot to %s", incoming_dir);
|
||||
setuid(uid);
|
||||
close(pipefds[0]);
|
||||
do_unpack(pipefds[1]);
|
||||
exit(0);
|
||||
default:;
|
||||
}
|
||||
|
||||
setuid(uid);
|
||||
close(pipefds[1]);
|
||||
wait_for_child(pipefds[0]);
|
||||
|
||||
return 0;
|
||||
}
|
46
appvm/qvm-copy-to-vm2
Executable file
46
appvm/qvm-copy-to-vm2
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
if [ $# -lt 2 ] ; then
|
||||
echo usage: $0 'vmname file [file]*'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILECOPY_SPOOL=/home/user/.filecopyspool
|
||||
if ! [ -e $FILECOPY_SPOOL ] ; then
|
||||
mkdir $FILECOPY_SPOOL
|
||||
fi
|
||||
|
||||
REQ_FILE_TMP=$FILECOPY_SPOOL/.req.$$
|
||||
echo -ne "$1""\x00" > $REQ_FILE_TMP
|
||||
echo -ne "$PROGRESS_FILE""\x00" >> $REQ_FILE_TMP
|
||||
|
||||
shift
|
||||
for FILE in "$@" ; do
|
||||
if ! [ "X""${FILE:0:1}" = X/ ] ; then
|
||||
FILE="$PWD"/"$FILE"
|
||||
fi
|
||||
echo -ne "$FILE""\x00" >> $REQ_FILE_TMP
|
||||
done
|
||||
|
||||
mv $REQ_FILE_TMP $FILECOPY_SPOOL/req.$$
|
||||
echo -n FCPR > /var/run/qubes/qrexec_agent
|
48
appvm/qvm-copy-to-vm2.kde
Executable file
48
appvm/qvm-copy-to-vm2.kde
Executable file
@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
VM=$(kdialog -inputbox "Enter the VM name to send files to:")
|
||||
if [ X$VM = X ] ; then exit 0 ; fi
|
||||
|
||||
SIZE=$(du -c "$@" | tail -1 | cut -f 1)
|
||||
REF=$(kdialog --progressbar "Copy progress")
|
||||
qdbus $REF org.freedesktop.DBus.Properties.Set "" maximum $SIZE
|
||||
|
||||
export PROGRESS_FILE=$(mktemp)
|
||||
qvm-copy-to-vm2 $VM "$@"
|
||||
while ! [ -s $PROGRESS_FILE ] ; do
|
||||
sleep 0.1
|
||||
done
|
||||
while true ; do
|
||||
read agentpid sentsize agentstatus < $PROGRESS_FILE
|
||||
if ! [ -e /proc/$agentpid ] ; then break ; fi
|
||||
if [ "x"$agentstatus = xdone ] ; then break ; fi
|
||||
CURRSIZE=$(($sentsize/1024))
|
||||
qdbus $REF org.freedesktop.DBus.Properties.Set "" value $CURRSIZE
|
||||
sleep 0.4
|
||||
done
|
||||
|
||||
qdbus $REF close
|
||||
rm -f $PROGRESS_FILE
|
||||
if ! [ "x"$agentstatus = xDONE ] ; then
|
||||
kdialog --sorry 'Abnormal file copy termination; see /var/log/qubes/qrexec.xid.log in dom0 for more details'
|
||||
fi
|
@ -4,7 +4,7 @@ Type=Service
|
||||
X-KDE-ServiceTypes=KonqPopupMenu/Plugin,inode/directory,all/allfiles
|
||||
|
||||
[Desktop Action QvmCopy]
|
||||
Exec=/usr/lib/qubes/qvm-copy-to-vm.kde %U
|
||||
Exec=/usr/lib/qubes/qvm-copy-to-vm2.kde %U
|
||||
Icon=kget
|
||||
Name=Send To VM
|
||||
|
||||
|
@ -4,7 +4,7 @@ Type=Service
|
||||
X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles
|
||||
|
||||
[Desktop Action QvmDvm]
|
||||
Exec=/usr/bin/qvm-open-in-dvm disposable %U
|
||||
Exec=/usr/bin/qvm-open-in-dvm2 %U
|
||||
Icon=kget
|
||||
Name=Open In DisposableVM
|
||||
|
||||
|
40
appvm/qvm-open-in-dvm2
Executable file
40
appvm/qvm-open-in-dvm2
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
if ! [ $# = 1 ] ; then
|
||||
echo "Usage: $0 filename"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILE="$1"
|
||||
if ! [ "X""${FILE:0:1}" = X/ ] ; then
|
||||
FILE="$PWD"/"$1"
|
||||
fi
|
||||
|
||||
DVMSPOOL=/home/user/.dvmspool
|
||||
if ! [ -e $DVMSPOOL ] ; then
|
||||
mkdir $DVMSPOOL || exit 1
|
||||
fi
|
||||
|
||||
echo -n "$FILE" > $DVMSPOOL/req.$$
|
||||
echo -n DVMR > /var/run/qubes/qrexec_agent
|
||||
|
105
appvm/unpack.c
Normal file
105
appvm/unpack.c
Normal file
@ -0,0 +1,105 @@
|
||||
#include <errno.h>
|
||||
#include <ioall.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "filecopy.h"
|
||||
|
||||
char namebuf[MAX_PATH_LENGTH];
|
||||
void notify_progress(int p1, int p2)
|
||||
{
|
||||
}
|
||||
|
||||
int global_status_fd;
|
||||
void do_exit(int code)
|
||||
{
|
||||
int codebuf = code;
|
||||
write(global_status_fd, &codebuf, sizeof codebuf);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
void fix_times_and_perms(struct file_header *hdr, char *name)
|
||||
{
|
||||
struct timeval times[2] =
|
||||
{ {hdr->atime, hdr->atime_nsec / 1000}, {hdr->mtime,
|
||||
hdr->mtime_nsec / 1000}
|
||||
};
|
||||
if (chmod(name, hdr->mode & 07777))
|
||||
do_exit(errno);
|
||||
if (utimes(name, times))
|
||||
do_exit(errno);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void process_one_file_reg(struct file_header *hdr, char *name)
|
||||
{
|
||||
char *ret;
|
||||
int fdout =
|
||||
open(name, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0700);
|
||||
if (fdout < 0)
|
||||
do_exit(errno);
|
||||
ret = copy_file(fdout, 0, hdr->filelen);
|
||||
if (ret)
|
||||
do_exit(errno);
|
||||
close(fdout);
|
||||
fix_times_and_perms(hdr, name);
|
||||
}
|
||||
|
||||
|
||||
void process_one_file_dir(struct file_header *hdr, char *name)
|
||||
{
|
||||
// fix perms only when the directory is sent for the second time
|
||||
// it allows to transfer r.x directory contents, as we create it rwx initially
|
||||
if (!mkdir(name, 0700))
|
||||
return;
|
||||
if (errno != EEXIST)
|
||||
do_exit(errno);
|
||||
fix_times_and_perms(hdr, name);
|
||||
}
|
||||
|
||||
void process_one_file_link(struct file_header *hdr, char *name)
|
||||
{
|
||||
char content[MAX_PATH_LENGTH];
|
||||
if (hdr->filelen > MAX_PATH_LENGTH - 1)
|
||||
do_exit(ENAMETOOLONG);
|
||||
if (!read_all(0, content, hdr->filelen))
|
||||
do_exit(errno);
|
||||
content[hdr->filelen] = 0;
|
||||
if (symlink(content, name))
|
||||
do_exit(errno);
|
||||
|
||||
}
|
||||
|
||||
void process_one_file(struct file_header *hdr)
|
||||
{
|
||||
if (hdr->namelen > MAX_PATH_LENGTH - 1)
|
||||
do_exit(ENAMETOOLONG);
|
||||
if (!read_all(0, namebuf, hdr->namelen))
|
||||
do_exit(errno);
|
||||
namebuf[hdr->namelen] = 0;
|
||||
if (S_ISREG(hdr->mode))
|
||||
process_one_file_reg(hdr, namebuf);
|
||||
else if (S_ISLNK(hdr->mode))
|
||||
process_one_file_link(hdr, namebuf);
|
||||
else if (S_ISDIR(hdr->mode))
|
||||
process_one_file_dir(hdr, namebuf);
|
||||
else
|
||||
do_exit(EINVAL);
|
||||
}
|
||||
|
||||
void do_unpack(int fd)
|
||||
{
|
||||
global_status_fd = fd;
|
||||
struct file_header hdr;
|
||||
while (read_all(0, &hdr, sizeof hdr))
|
||||
process_one_file(&hdr);
|
||||
if (errno)
|
||||
do_exit(errno);
|
||||
else
|
||||
do_exit(LEGAL_EOF);
|
||||
}
|
50
common/gui-fatal.c
Normal file
50
common/gui-fatal.c
Normal file
@ -0,0 +1,50 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
static void fix_display()
|
||||
{
|
||||
setenv("DISPLAY", ":0", 1);
|
||||
}
|
||||
|
||||
static void produce_message(char * type, const char *fmt, va_list args)
|
||||
{
|
||||
char *kdialog_msg;
|
||||
char buf[1024];
|
||||
(void) vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
asprintf(&kdialog_msg, "%s: %s: %s (error type: %s)",
|
||||
program_invocation_short_name, type, buf, strerror(errno));
|
||||
fprintf(stderr, "%s", kdialog_msg);
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
exit(1); //what else
|
||||
case 0:
|
||||
fix_display();
|
||||
execlp("kdialog", "kdialog", "--sorry", kdialog_msg, NULL);
|
||||
exit(1);
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
void gui_fatal(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
produce_message("Fatal error", fmt, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void gui_nonfatal(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
produce_message("Information", fmt, args);
|
||||
va_end(args);
|
||||
}
|
2
common/gui-fatal.h
Normal file
2
common/gui-fatal.h
Normal file
@ -0,0 +1,2 @@
|
||||
void gui_fatal(const char *fmt, ...);
|
||||
void gui_nonfatal(const char *fmt, ...);
|
97
common/ioall.c
Normal file
97
common/ioall.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
void perror_wrapper(char * msg)
|
||||
{
|
||||
int prev=errno;
|
||||
perror(msg);
|
||||
errno=prev;
|
||||
}
|
||||
|
||||
|
||||
int write_all(int fd, void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = write(fd, (char *) buf + written, size - written);
|
||||
if (ret == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (ret <= 0) {
|
||||
perror_wrapper("write");
|
||||
return 0;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "sent %d bytes\n", size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_all(int fd, void *buf, int size)
|
||||
{
|
||||
int got_read = 0;
|
||||
int ret;
|
||||
while (got_read < size) {
|
||||
ret = read(fd, (char *) buf + got_read, size - got_read);
|
||||
if (ret == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (ret == 0) {
|
||||
errno = 0;
|
||||
fprintf(stderr, "EOF\n");
|
||||
return 0;
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror_wrapper("read");
|
||||
return 0;
|
||||
}
|
||||
got_read += ret;
|
||||
}
|
||||
// fprintf(stderr, "read %d bytes\n", size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int copy_fd_all(int fdout, int fdin)
|
||||
{
|
||||
int ret;
|
||||
char buf[4096];
|
||||
for (;;) {
|
||||
ret = read(fdin, buf, sizeof(buf));
|
||||
if (ret == -1 && errno == EINTR)
|
||||
continue;
|
||||
if (!ret)
|
||||
break;
|
||||
if (ret < 0) {
|
||||
perror_wrapper("read");
|
||||
return 0;
|
||||
}
|
||||
if (!write_all(fdout, buf, ret)) {
|
||||
perror_wrapper("write");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
3
common/ioall.h
Normal file
3
common/ioall.h
Normal file
@ -0,0 +1,3 @@
|
||||
int write_all(int fd, void *buf, int size);
|
||||
int read_all(int fd, void *buf, int size);
|
||||
int copy_fd_all(int fdout, int fdin);
|
@ -54,6 +54,8 @@ start()
|
||||
fi
|
||||
fi
|
||||
|
||||
/usr/lib/qubes/qrexec_agent 2>/var/log/qubes/qrexec_agent.log &
|
||||
|
||||
[ -x /rw/config/rc.local ] && /rw/config/rc.local
|
||||
success
|
||||
echo ""
|
||||
|
@ -1306,17 +1306,15 @@ class QubesProxyVm(QubesNetVm):
|
||||
iptables += "# '{0}' VM:\n".format(vm.name)
|
||||
iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid)
|
||||
|
||||
|
||||
accept_action = "ACCEPT"
|
||||
reject_action = "REJECT --reject-with icmp-host-prohibited"
|
||||
|
||||
if conf["allow"]:
|
||||
rules_action = accept_action
|
||||
default_action = reject_action
|
||||
iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid)
|
||||
else:
|
||||
rules_action = reject_action
|
||||
default_action = accept_action
|
||||
rules_action = reject_action
|
||||
else:
|
||||
default_action = reject_action
|
||||
rules_action = accept_action
|
||||
|
||||
for rule in conf["rules"]:
|
||||
iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"])
|
||||
@ -1334,6 +1332,8 @@ class QubesProxyVm(QubesNetVm):
|
||||
# PREROUTING does DNAT to NetVM DNSes, so we need self.netvm_vm. properties
|
||||
iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.gateway)
|
||||
iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.secondary_dns)
|
||||
if conf["allowIcmp"]:
|
||||
iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid)
|
||||
|
||||
iptables += "-A FORWARD -i vif{0}.0 -j {1}\n".format(xid, default_action)
|
||||
|
||||
@ -1438,7 +1438,7 @@ class QubesDisposableVm(QubesVm):
|
||||
|
||||
template_vm = kwargs.pop("template_vm")
|
||||
|
||||
super(QubesDisposableVm, self).__init__(dir_path=None, **kwargs)
|
||||
super(QubesDisposableVm, self).__init__(dir_path="/nonexistent", **kwargs)
|
||||
qid = kwargs["qid"]
|
||||
|
||||
assert template_vm is not None, "Missing template_vm for DisposableVM!"
|
||||
@ -1503,7 +1503,8 @@ class QubesAppVm(QubesCowVm):
|
||||
root = xml.etree.ElementTree.Element(
|
||||
"QubesFirwallRules",
|
||||
policy = "allow" if conf["allow"] else "deny",
|
||||
dns = "allow" if conf["allowDns"] else "deny"
|
||||
dns = "allow" if conf["allowDns"] else "deny",
|
||||
icmp = "allow" if conf["allowIcmp"] else "deny"
|
||||
)
|
||||
|
||||
for rule in conf["rules"]:
|
||||
@ -1537,7 +1538,7 @@ class QubesAppVm(QubesCowVm):
|
||||
return True
|
||||
|
||||
def get_firewall_conf(self):
|
||||
conf = { "rules": list(), "allow": True, "allowDns": True }
|
||||
conf = { "rules": list(), "allow": True, "allowDns": True, "allowIcmp": True }
|
||||
|
||||
try:
|
||||
tree = xml.etree.ElementTree.parse(self.firewall_conf)
|
||||
@ -1545,6 +1546,7 @@ class QubesAppVm(QubesCowVm):
|
||||
|
||||
conf["allow"] = (root.get("policy") == "allow")
|
||||
conf["allowDns"] = (root.get("dns") == "allow")
|
||||
conf["allowIcmp"] = (root.get("icmp") == "allow")
|
||||
|
||||
for element in root:
|
||||
rule = {}
|
||||
|
@ -29,10 +29,14 @@ import socket
|
||||
import errno
|
||||
import dbus
|
||||
import time
|
||||
import os
|
||||
import os.path
|
||||
|
||||
qubes_guid_path = "/usr/bin/qubes_guid"
|
||||
qubes_clipd_path = "/usr/bin/qclipd"
|
||||
qubes_qfilexchgd_path= "/usr/bin/qfilexchgd"
|
||||
qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon"
|
||||
qrexec_client_path = "/usr/lib/qubes/qrexec_client"
|
||||
notify_object = None
|
||||
|
||||
# how long (in sec) to wait for VMs to shutdown
|
||||
@ -45,6 +49,15 @@ def tray_notify(str, label, timeout = 3000):
|
||||
def tray_notify_error(str, timeout = 3000):
|
||||
notify_object.Notify("Qubes", 0, "dialog-error", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
|
||||
|
||||
def actually_execute(domid, cmd, options):
|
||||
args = [qrexec_client_path, "-d", domid, cmd]
|
||||
if options.localcmd is not None:
|
||||
args += [ "-l", options.localcmd]
|
||||
if options.passio and not options.run_on_all_running:
|
||||
os.execv(qrexec_client_path, args)
|
||||
exit(1)
|
||||
args += ["-e"]
|
||||
subprocess.call(args)
|
||||
|
||||
def vm_run_cmd(vm, cmd, options):
|
||||
if options.shutdown:
|
||||
@ -78,6 +91,7 @@ def vm_run_cmd(vm, cmd, options):
|
||||
if options.tray:
|
||||
tray_notify ("Starting the '{0}' VM...".format(vm.name), label=vm.label)
|
||||
xid = vm.start(verbose=options.verbose)
|
||||
|
||||
except (IOError, OSError, QubesException) as err:
|
||||
print "ERROR: {0}".format(err)
|
||||
if options.tray:
|
||||
@ -90,39 +104,37 @@ def vm_run_cmd(vm, cmd, options):
|
||||
subprocess.call(["kdialog", "--error", "Not enough memory to start '{0}' VM! Close one or more running VMs and try again.".format(vm.name)])
|
||||
exit (1)
|
||||
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes GUId..."
|
||||
if os.getenv("DISPLAY") is not None:
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes GUId..."
|
||||
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qubes_guid!"
|
||||
if options.tray:
|
||||
tray_notify_error ("ERROR: Cannot start qubes_guid!")
|
||||
exit (1)
|
||||
else: # VM already running...
|
||||
guid_is_running = True
|
||||
xid = vm.get_xid()
|
||||
s = socket.socket (socket.AF_UNIX)
|
||||
try:
|
||||
s.connect ("/var/run/qubes/cmd_socket.{0}".format(xid))
|
||||
except (IOError, OSError) as e:
|
||||
if e.errno in [errno.ENOENT,errno.ECONNREFUSED]:
|
||||
guid_is_running = False
|
||||
else:
|
||||
print "ERROR: unix-connect: {0}".format(e)
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qubes_guid!"
|
||||
if options.tray:
|
||||
tray_notify_error ("ERROR: Cannot connect to GUI daemon for this VM!")
|
||||
exit(1)
|
||||
if guid_is_running:
|
||||
s.send (cmd)
|
||||
s.close()
|
||||
else:
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
tray_notify_error ("ERROR: Cannot start qubes_guid!")
|
||||
exit (1)
|
||||
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes rexec daemon..."
|
||||
|
||||
retcode = subprocess.call ([qrexec_daemon_path, str(xid)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qrexec_daemon!"
|
||||
exit (1)
|
||||
|
||||
actually_execute(str(xid), cmd, options);
|
||||
|
||||
else: # VM already running...
|
||||
xid = vm.get_xid()
|
||||
if os.getenv("DISPLAY") is not None and not os.path.isfile("/var/run/qubes/guid_running.{0}".format(xid)):
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qubes_guid!"
|
||||
if options.tray:
|
||||
tray_notify_error ("ERROR: Cannot start the GUI daemon for this VM!")
|
||||
exit (1)
|
||||
actually_execute(str(xid), cmd, options);
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] [<vm-name>] [<cmd>]"
|
||||
@ -153,6 +165,11 @@ def main():
|
||||
parser.add_option ("--unpause", action="store_true", dest="unpause", default=False,
|
||||
help="Do 'xm unpause' for the VM(s) (can be combined this with --all and --wait)")
|
||||
|
||||
parser.add_option ("--pass_io", action="store_true", dest="passio", default=False,
|
||||
help="Pass stdin/stdout/stderr from remote program")
|
||||
|
||||
parser.add_option ("--localcmd", action="store", dest="localcmd", default=None,
|
||||
help="With --pass_io, pass stdin/stdout/stderr to the given program")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
|
@ -24,8 +24,10 @@ from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
qubes_guid_path = "/usr/bin/qubes_guid"
|
||||
qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon"
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <vm-name>"
|
||||
@ -33,6 +35,8 @@ def main():
|
||||
parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
parser.add_option ("--no-guid", action="store_true", dest="noguid", default=False,
|
||||
help="Do not start the GUId")
|
||||
parser.add_option ("--no-rexec", action="store_true", dest="norexec", default=False,
|
||||
help="Do not start rexec")
|
||||
parser.add_option ("--console", action="store_true", dest="debug_console", default=False,
|
||||
help="Attach debugging console to the newly started VM")
|
||||
parser.add_option ("--dvm", action="store_true", dest="preparing_dvm", default=False,
|
||||
@ -60,15 +64,23 @@ def main():
|
||||
print "ERROR: {0}".format(err)
|
||||
exit (1)
|
||||
|
||||
if options.noguid:
|
||||
exit (0)
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes GUId..."
|
||||
if not options.noguid and os.getenv("DISPLAY") is not None:
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes GUId..."
|
||||
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qubes_guid!"
|
||||
exit (1)
|
||||
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon, "-l", str(vm.label.index)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qubes_guid!"
|
||||
exit (1)
|
||||
|
||||
if not options.norexec:
|
||||
if options.verbose:
|
||||
print "--> Starting Qubes rexec..."
|
||||
|
||||
retcode = subprocess.call ([qrexec_daemon_path, str(xid)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qrexec_daemon!"
|
||||
exit (1)
|
||||
|
||||
|
||||
main()
|
||||
|
@ -28,7 +28,7 @@ hexnumber()
|
||||
process()
|
||||
{
|
||||
if ! [ "x""$1" = "xfile" ] ; then
|
||||
exec /etc/xen/scripts/block "$@"
|
||||
exec /etc/xen/scripts/block $ORIG_ARGS
|
||||
fi
|
||||
while true ; do
|
||||
dev=$(losetup -f --show $2)
|
||||
@ -48,6 +48,8 @@ XENBUS_PATH="${XENBUS_PATH:?}"
|
||||
if ! [ "$1" = "add" ] || ! [ -f /var/run/qubes/fast_block_attach ] ; then
|
||||
exec /etc/xen/scripts/block "$@"
|
||||
fi
|
||||
|
||||
ORIG_ARGS="$@"
|
||||
|
||||
vars=$(xenstore-read "$XENBUS_PATH/type" "$XENBUS_PATH/params")
|
||||
process $vars
|
||||
|
59
dom0/restore/qfile-daemon
Executable file
59
dom0/restore/qfile-daemon
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python2.6
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from qubes.qubes import QubesVmCollection
|
||||
|
||||
def is_copy_allowed(vm):
|
||||
# if vm.copy_allowed:
|
||||
# return True
|
||||
q = 'Do you authorize file copy from '
|
||||
q+= os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
q+= ' to ' + vm.name + ' ?'
|
||||
retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', q, '--title', 'File transfer confirmation'])
|
||||
return retcode == 0
|
||||
|
||||
def main():
|
||||
FILECOPY_VMNAME_SIZE = 32
|
||||
blob=os.read(0, FILECOPY_VMNAME_SIZE)
|
||||
vmname = blob.split("\x00")[0]
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
# we do not want to flood dom0 with error windows; so just log to stderr
|
||||
if vm is None:
|
||||
print >> sys.stderr, 'Domain ' + vmname + ' does not exist ?'
|
||||
exit(1)
|
||||
if not vm.is_running():
|
||||
print >> sys.stderr, 'Domain ' + vmname + ' is not running ?'
|
||||
exit(1)
|
||||
if not is_copy_allowed(vm):
|
||||
exit(1)
|
||||
cmd = "root:/usr/lib/qubes/qfile-unpacker " + os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
os.execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d", vmname, cmd)
|
||||
|
||||
main()
|
133
dom0/restore/qfile-daemon-dvm
Executable file
133
dom0/restore/qfile-daemon-dvm
Executable file
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/python2.6
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import os
|
||||
import dbus
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubes import QubesDaemonPidfile
|
||||
from qubes.qmemman_client import QMemmanClient
|
||||
|
||||
current_savefile = '/var/run/qubes/current_savefile'
|
||||
notify_object = None
|
||||
|
||||
class QfileDaemonDvm:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def do_get_dvm(self):
|
||||
qmemman_client = QMemmanClient()
|
||||
if not qmemman_client.request_memory(400*1024*1024):
|
||||
qmemman_client.close()
|
||||
errmsg = 'Not enough memory to create DVM. '
|
||||
errmsg +='Terminate some appVM and retry.'
|
||||
subprocess.call(['/usr/bin/kdialog', '--sorry', errmsg])
|
||||
return None
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(self.name)
|
||||
if vm is None:
|
||||
sys.stderr.write( 'Domain ' + vmname + ' does not exist ?')
|
||||
qvm_collection.unlock_db()
|
||||
qmemman_client.close()
|
||||
return None
|
||||
retcode = subprocess.call(['/usr/lib/qubes/qubes_restore',
|
||||
current_savefile,
|
||||
'-c', vm.label.color,
|
||||
'-i', vm.label.icon,
|
||||
'-l', str(vm.label.index)])
|
||||
qmemman_client.close()
|
||||
if retcode != 0:
|
||||
subprocess.call(['/usr/bin/kdialog', '--sorry', 'DisposableVM creation failed, see qubes_restore.log'])
|
||||
qvm_collection.unlock_db()
|
||||
return None
|
||||
f = open('/var/run/qubes/dispVM_xid', 'r');
|
||||
disp_xid = f.readline().rstrip('\n')
|
||||
disp_name = f.readline().rstrip('\n')
|
||||
disptempl = f.readline().rstrip('\n')
|
||||
f.close()
|
||||
vm_disptempl = qvm_collection.get_vm_by_name(disptempl);
|
||||
if vm_disptempl is None:
|
||||
sys.stderr.write( 'Domain ' + disptempl + ' does not exist ?')
|
||||
qvm_collection.unlock_db()
|
||||
return None
|
||||
qvm_collection.add_new_disposablevm(disp_name, vm_disptempl.template_vm, label=vm.label)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
return disp_name
|
||||
|
||||
def dvm_setup_ok(self):
|
||||
dvmdata_dir = '/var/lib/qubes/dvmdata/'
|
||||
if not os.path.isfile(current_savefile):
|
||||
return False
|
||||
if not os.path.isfile(dvmdata_dir+'default_savefile') or not os.path.isfile(dvmdata_dir+'savefile_root'):
|
||||
return False
|
||||
dvm_mtime = os.stat(current_savefile).st_mtime
|
||||
root_mtime = os.stat(dvmdata_dir+'savefile_root').st_mtime
|
||||
if dvm_mtime < root_mtime:
|
||||
return False
|
||||
return True
|
||||
|
||||
def tray_notify(self, str, timeout = 3000):
|
||||
notify_object.Notify("Qubes", 0, "red", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
|
||||
|
||||
def tray_notify_error(self, str, timeout = 3000):
|
||||
notify_object.Notify("Qubes", 0, "dialog-error", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
|
||||
|
||||
def get_dvm(self):
|
||||
if not self.dvm_setup_ok():
|
||||
self.tray_notify("Updating DisposableVM savefile, please wait")
|
||||
if os.system("qvm-create-default-dvm --default-template --default-script >/var/run/qubes/qvm-create-default-dvm.stdout </dev/null" ) != 0:
|
||||
self.tray_notify_error("DVM savefile creation failed")
|
||||
return None
|
||||
return self.do_get_dvm()
|
||||
|
||||
def remove_disposable_from_qdb(self, name):
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
vm = qvm_collection.get_vm_by_name(name)
|
||||
if vm is None:
|
||||
qvm_collection.unlock_db()
|
||||
return False
|
||||
qvm_collection.pop(vm.qid)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
def main():
|
||||
global notify_object
|
||||
notify_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
|
||||
qfile = QfileDaemonDvm(os.getenv("QREXEC_REMOTE_DOMAIN"))
|
||||
dispname = qfile.get_dvm()
|
||||
if dispname is not None:
|
||||
subprocess.call(['/usr/lib/qubes/qrexec_client', '-d', dispname, 'directly:user:/usr/lib/qubes/dvm_file_editor'])
|
||||
subprocess.call(['/usr/sbin/xm', 'destroy', dispname])
|
||||
qfile.remove_disposable_from_qdb(dispname)
|
||||
|
||||
main()
|
||||
|
@ -21,7 +21,7 @@ if ! [ -d $VMDIR ] ; then
|
||||
echo $VMDIR does not exist ?
|
||||
exit 1
|
||||
fi
|
||||
if ! qvm-start $1 --no-guid --dvm ; then
|
||||
if ! qvm-start $1 --no-guid --no-rexec --dvm ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -182,6 +182,29 @@ int xend_connect()
|
||||
return s;
|
||||
}
|
||||
|
||||
void start_rexec(int domid)
|
||||
{
|
||||
int pid, status;
|
||||
char dstr[40];
|
||||
snprintf(dstr, sizeof(dstr), "%d", domid);
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
execl("/usr/lib/qubes/qrexec_daemon", "qrexec_daemon",
|
||||
dstr, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
default:;
|
||||
}
|
||||
if (waitpid(pid, &status, 0) < 0) {
|
||||
perror("waitpid");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void start_guid(int domid, int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
@ -197,6 +220,7 @@ void start_guid(int domid, int argc, char **argv)
|
||||
execv("/usr/bin/qubes_guid", guid_args);
|
||||
perror("execv");
|
||||
}
|
||||
|
||||
// modify the savefile. fd = fd to the open savefile,
|
||||
// buf - already read 1st page of the savefile
|
||||
// pattern - pattern to search for
|
||||
@ -452,10 +476,11 @@ int main(int argc, char **argv)
|
||||
resp = recv_resp(fd);
|
||||
// printf("%s\n", resp);
|
||||
fprintf(stderr, "time=%s, creating xenstore entries\n", gettime());
|
||||
#endif
|
||||
#endif
|
||||
setup_xenstore(netvm_id, domid, dispid, name);
|
||||
fprintf(stderr, "time=%s, starting qubes_guid\n", gettime());
|
||||
rm_fast_flag();
|
||||
start_rexec(domid);
|
||||
start_guid(domid, argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ if ! [ -d "/var/lib/qubes/vm-templates/$TEMPLATENAME" ] ; then
|
||||
exit 1
|
||||
fi
|
||||
DVMTMPL="$TEMPLATENAME"-dvm
|
||||
if ! [ -d "/var/lib/qubes/appvms/$DVMTMPL" ] ; then
|
||||
DVMTMPLDIR="/var/lib/qubes/appvms/$DVMTMPL"
|
||||
if ! [ -d "$DVMTMPLDIR" ] ; then
|
||||
if ! qvm-create -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi
|
||||
fi
|
||||
if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \
|
||||
@ -53,3 +54,7 @@ else
|
||||
chmod 660 $SHMCOPY
|
||||
ln -s $SHMCOPY $CURRENT
|
||||
fi
|
||||
|
||||
chgrp qubes "$DVMTMPLDIR" "$DVMTMPLDIR"/*
|
||||
chmod 660 "$DVMTMPLDIR"/*
|
||||
chmod 770 "$DVMTMPLDIR"
|
||||
|
@ -32,6 +32,7 @@ start()
|
||||
/usr/lib/qubes/qubes_setup_dnat_to_ns
|
||||
echo "1" > /proc/sys/net/ipv4/ip_forward
|
||||
fi
|
||||
|
||||
success
|
||||
echo ""
|
||||
return 0
|
||||
|
14
qrexec/Makefile
Normal file
14
qrexec/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
CC=gcc
|
||||
CFLAGS+=-g -Wall -I../vchan -I../common
|
||||
XENLIBS=-lvchan -lu2mfn -lxenstore -lxenctrl
|
||||
COMMONIOALL=../common/ioall.o
|
||||
|
||||
all: qrexec_daemon qrexec_agent qrexec_client
|
||||
qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o
|
||||
$(CC) -L../vchan -L../u2mfn -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
|
||||
qrexec_agent: qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL)
|
||||
$(CC) -L../vchan -L../u2mfn -g -o qrexec_agent qrexec_agent.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) $(XENLIBS)
|
||||
qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o
|
||||
$(CC) -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o
|
||||
clean:
|
||||
rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client
|
93
qrexec/buffer.c
Normal file
93
qrexec/buffer.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
|
||||
#define BUFFER_LIMIT 50000000
|
||||
static int total_mem;
|
||||
static char *limited_malloc(int len)
|
||||
{
|
||||
char *ret;
|
||||
total_mem += len;
|
||||
if (total_mem > BUFFER_LIMIT) {
|
||||
fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n");
|
||||
exit(1);
|
||||
}
|
||||
ret = malloc(len);
|
||||
if (!ret) {
|
||||
perror("malloc");
|
||||
exit(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void limited_free(char *ptr, int len)
|
||||
{
|
||||
free(ptr);
|
||||
total_mem -= len;
|
||||
}
|
||||
|
||||
void buffer_init(struct buffer *b)
|
||||
{
|
||||
b->buflen = 0;
|
||||
b->data = NULL;
|
||||
}
|
||||
|
||||
void buffer_free(struct buffer *b)
|
||||
{
|
||||
if (b->buflen)
|
||||
limited_free(b->data, b->buflen);
|
||||
buffer_init(b);
|
||||
}
|
||||
|
||||
void buffer_append(struct buffer *b, char *data, int len)
|
||||
{
|
||||
int newsize = len + b->buflen;
|
||||
char *qdata = limited_malloc(len + b->buflen);
|
||||
memcpy(qdata, b->data, b->buflen);
|
||||
memcpy(qdata + b->buflen, data, len);
|
||||
buffer_free(b);
|
||||
b->buflen = newsize;
|
||||
b->data = qdata;
|
||||
}
|
||||
|
||||
void buffer_remove(struct buffer *b, int len)
|
||||
{
|
||||
int newsize = b->buflen - len;
|
||||
char *qdata = limited_malloc(newsize);
|
||||
memcpy(qdata, b->data + len, newsize);
|
||||
buffer_free(b);
|
||||
b->buflen = newsize;
|
||||
b->data = qdata;
|
||||
}
|
||||
|
||||
int buffer_len(struct buffer *b)
|
||||
{
|
||||
return b->buflen;
|
||||
}
|
||||
|
||||
void *buffer_data(struct buffer *b)
|
||||
{
|
||||
return b->data;
|
||||
}
|
32
qrexec/buffer.h
Normal file
32
qrexec/buffer.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
struct buffer {
|
||||
char *data;
|
||||
int buflen;
|
||||
};
|
||||
|
||||
void buffer_init(struct buffer *b);
|
||||
void buffer_free(struct buffer *b);
|
||||
void buffer_append(struct buffer *b, char *data, int len);
|
||||
void buffer_remove(struct buffer *b, int len);
|
||||
int buffer_len(struct buffer *b);
|
||||
void *buffer_data(struct buffer *b);
|
74
qrexec/exec.c
Normal file
74
qrexec/exec.c
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern void do_exec(char *);
|
||||
|
||||
void fix_fds(int fdin, int fdout, int fderr)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
if (i != fdin && i != fdout && i != fderr)
|
||||
close(i);
|
||||
dup2(fdin, 0);
|
||||
dup2(fdout, 1);
|
||||
dup2(fderr, 2);
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
if (fderr != 2)
|
||||
close(fderr);
|
||||
}
|
||||
|
||||
void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||
int *stderr_fd)
|
||||
{
|
||||
int inpipe[2], outpipe[2], errpipe[2];
|
||||
|
||||
if (pipe(inpipe) || pipe(outpipe) || (stderr_fd && pipe(errpipe))) {
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
switch (*pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(-1);
|
||||
case 0:
|
||||
if (stderr_fd) {
|
||||
fix_fds(inpipe[0], outpipe[1], errpipe[1]);
|
||||
} else
|
||||
fix_fds(inpipe[0], outpipe[1], 2);
|
||||
|
||||
do_exec(cmdline);
|
||||
exit(-1);
|
||||
default:;
|
||||
}
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
*stdin_fd = inpipe[1];
|
||||
*stdout_fd = outpipe[0];
|
||||
if (stderr_fd) {
|
||||
close(errpipe[1]);
|
||||
*stderr_fd = errpipe[0];
|
||||
}
|
||||
}
|
48
qrexec/glue.h
Normal file
48
qrexec/glue.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd,
|
||||
int *stderr_fd);
|
||||
int peer_server_init(int port);
|
||||
char *peer_client_init(int dom, int port);
|
||||
void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset);
|
||||
int read_ready_vchan_ext();
|
||||
int read_all_vchan_ext(void *buf, int size);
|
||||
int write_all_vchan_ext(void *buf, int size);
|
||||
int buffer_space_vchan_ext();
|
||||
void fix_fds(int fdin, int fdout, int fderr);
|
||||
|
||||
int get_server_socket(int domid, char * domname);
|
||||
int do_accept(int s);
|
||||
|
||||
enum {
|
||||
WRITE_STDIN_OK = 0x200,
|
||||
WRITE_STDIN_BUFFERED,
|
||||
WRITE_STDIN_ERROR
|
||||
};
|
||||
|
||||
int flush_client_data(int fd, int clid, struct buffer *buffer);
|
||||
int write_stdin(int fd, int clid, char *data, int len,
|
||||
struct buffer *buffer);
|
||||
void set_nonblock(int fd);
|
||||
int fork_and_flush_stdin(int fd, struct buffer *buffer);
|
66
qrexec/qrexec.h
Normal file
66
qrexec/qrexec.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#define QREXEC_DAEMON_SOCKET_DIR "/var/run/qubes"
|
||||
#define MAX_FDS 256
|
||||
#define MAX_DATA_CHUNK 4096
|
||||
|
||||
#define REXEC_PORT 512
|
||||
|
||||
#define QREXEC_AGENT_TRIGGER_PATH "/var/run/qubes/qrexec_agent"
|
||||
|
||||
enum {
|
||||
MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100,
|
||||
MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||
|
||||
MSG_SERVER_TO_AGENT_EXEC_CMDLINE,
|
||||
MSG_SERVER_TO_AGENT_JUST_EXEC,
|
||||
MSG_SERVER_TO_AGENT_INPUT,
|
||||
MSG_SERVER_TO_AGENT_CLIENT_END,
|
||||
|
||||
MSG_XOFF,
|
||||
MSG_XON,
|
||||
|
||||
MSG_AGENT_TO_SERVER_STDOUT,
|
||||
MSG_AGENT_TO_SERVER_STDERR,
|
||||
MSG_AGENT_TO_SERVER_EXIT_CODE,
|
||||
MSG_AGENT_TO_SERVER_TRIGGER_EXEC,
|
||||
|
||||
MSG_SERVER_TO_CLIENT_STDOUT,
|
||||
MSG_SERVER_TO_CLIENT_STDERR,
|
||||
MSG_SERVER_TO_CLIENT_EXIT_CODE
|
||||
};
|
||||
|
||||
enum {
|
||||
QREXEC_EXECUTE_FILE_COPY=0x700,
|
||||
QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM
|
||||
};
|
||||
|
||||
struct server_header {
|
||||
unsigned int type;
|
||||
unsigned int clid;
|
||||
unsigned int len;
|
||||
};
|
||||
|
||||
struct client_header {
|
||||
unsigned int type;
|
||||
unsigned int len;
|
||||
};
|
539
qrexec/qrexec_agent.c
Normal file
539
qrexec/qrexec_agent.c
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/stat.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
enum fdtype {
|
||||
FDTYPE_INVALID,
|
||||
FDTYPE_STDOUT,
|
||||
FDTYPE_STDERR
|
||||
};
|
||||
|
||||
struct _process_fd {
|
||||
int clid;
|
||||
int type;
|
||||
int is_blocked;
|
||||
};
|
||||
struct _client_info {
|
||||
int stdin_fd;
|
||||
int stdout_fd;
|
||||
int stderr_fd;
|
||||
|
||||
int pid;
|
||||
int is_blocked;
|
||||
int is_close_after_flush_needed;
|
||||
struct buffer buffer;
|
||||
};
|
||||
|
||||
int max_process_fd = -1;
|
||||
|
||||
/* indexed by file descriptor */
|
||||
struct _process_fd process_fd[MAX_FDS];
|
||||
|
||||
/* indexed by client id, which is descriptor number of a client in daemon */
|
||||
struct _client_info client_info[MAX_FDS];
|
||||
|
||||
int trigger_fd;
|
||||
|
||||
void init()
|
||||
{
|
||||
peer_server_init(REXEC_PORT);
|
||||
umask(0);
|
||||
mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666);
|
||||
umask(077);
|
||||
trigger_fd =
|
||||
open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK);
|
||||
}
|
||||
|
||||
void no_colon_in_cmd()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"cmdline is supposed to be in user:command form\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void do_exec_directly(char *cmd)
|
||||
{
|
||||
struct passwd *pwd;
|
||||
char *sep = index(cmd, ':');
|
||||
if (!sep)
|
||||
no_colon_in_cmd();
|
||||
*sep = 0;
|
||||
pwd = getpwnam(cmd);
|
||||
if (!pwd) {
|
||||
perror("getpwnam");
|
||||
exit(1);
|
||||
}
|
||||
setgid(pwd->pw_gid);
|
||||
initgroups(cmd, pwd->pw_gid);
|
||||
setuid(pwd->pw_uid);
|
||||
setenv("HOME", pwd->pw_dir, 1);
|
||||
setenv("USER", cmd, 1);
|
||||
execl(sep + 1, sep + 1, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void do_exec(char *cmd)
|
||||
{
|
||||
char *sep = index(cmd, ':');
|
||||
if (!sep)
|
||||
no_colon_in_cmd();
|
||||
*sep = 0;
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
if (!strcmp(cmd, "directly"))
|
||||
do_exec_directly(sep + 1);
|
||||
execl("/bin/su", "su", "-", cmd, "-c", sep + 1, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void handle_just_exec(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
int fdn, pid;
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
fdn = open("/dev/null", O_RDWR);
|
||||
fix_fds(fdn, fdn, fdn);
|
||||
do_exec(buf);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
default:;
|
||||
}
|
||||
fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid);
|
||||
}
|
||||
|
||||
void handle_exec(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
int pid, stdin_fd, stdout_fd, stderr_fd;
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
|
||||
do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd);
|
||||
|
||||
process_fd[stdout_fd].clid = clid;
|
||||
process_fd[stdout_fd].type = FDTYPE_STDOUT;
|
||||
process_fd[stdout_fd].is_blocked = 0;
|
||||
process_fd[stderr_fd].clid = clid;
|
||||
process_fd[stderr_fd].type = FDTYPE_STDERR;
|
||||
process_fd[stderr_fd].is_blocked = 0;
|
||||
|
||||
if (stderr_fd > max_process_fd)
|
||||
max_process_fd = stderr_fd;
|
||||
if (stdout_fd > max_process_fd)
|
||||
max_process_fd = stdout_fd;
|
||||
|
||||
set_nonblock(stdin_fd);
|
||||
|
||||
client_info[clid].stdin_fd = stdin_fd;
|
||||
client_info[clid].stdout_fd = stdout_fd;
|
||||
client_info[clid].stderr_fd = stderr_fd;
|
||||
client_info[clid].pid = pid;
|
||||
client_info[clid].is_blocked = 0;
|
||||
client_info[clid].is_close_after_flush_needed = 0;
|
||||
buffer_init(&client_info[clid].buffer);
|
||||
|
||||
fprintf(stderr, "executed %s pid %d\n", buf, pid);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void update_max_process_fd()
|
||||
{
|
||||
int i;
|
||||
for (i = max_process_fd;
|
||||
process_fd[i].type == FDTYPE_INVALID && i >= 0; i--);
|
||||
max_process_fd = i;
|
||||
}
|
||||
|
||||
void send_exit_code(int clid, int status)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = sizeof status;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
write_all_vchan_ext(&status, sizeof(status));
|
||||
fprintf(stderr, "send exit code for clid %d pid %d\n", clid,
|
||||
client_info[clid].pid);
|
||||
}
|
||||
|
||||
|
||||
// erase process data structures, possibly forced by remote
|
||||
void remove_process(int clid, int status)
|
||||
{
|
||||
int i;
|
||||
if (!client_info[clid].pid)
|
||||
return;
|
||||
fork_and_flush_stdin(client_info[clid].stdin_fd, &client_info[clid].buffer);
|
||||
#if 0
|
||||
// let's let it die by itself, possibly after it has received buffered stdin
|
||||
kill(client_info[clid].pid, SIGKILL);
|
||||
#endif
|
||||
if (status != -1)
|
||||
send_exit_code(clid, status);
|
||||
|
||||
|
||||
close(client_info[clid].stdin_fd);
|
||||
client_info[clid].pid = 0;
|
||||
client_info[clid].stdin_fd = -1;
|
||||
client_info[clid].is_blocked = 0;
|
||||
buffer_free(&client_info[clid].buffer);
|
||||
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& process_fd[i].clid == clid) {
|
||||
process_fd[i].type = FDTYPE_INVALID;
|
||||
process_fd[i].clid = -1;
|
||||
process_fd[i].is_blocked = 0;
|
||||
close(i);
|
||||
}
|
||||
update_max_process_fd();
|
||||
}
|
||||
|
||||
void handle_input(int clid, int len)
|
||||
{
|
||||
char buf[len];
|
||||
|
||||
read_all_vchan_ext(buf, len);
|
||||
if (!client_info[clid].pid)
|
||||
return;
|
||||
|
||||
if (len == 0) {
|
||||
if (client_info[clid].is_blocked)
|
||||
client_info[clid].is_close_after_flush_needed = 1;
|
||||
else {
|
||||
close(client_info[clid].stdin_fd);
|
||||
client_info[clid].stdin_fd = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (write_stdin
|
||||
(client_info[clid].stdin_fd, clid, buf, len,
|
||||
&client_info[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
client_info[clid].is_blocked = 1;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
remove_process(clid, 128);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown write_stdin?\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void set_blocked_outerr(int clid, int val)
|
||||
{
|
||||
process_fd[client_info[clid].stdout_fd].is_blocked = val;
|
||||
process_fd[client_info[clid].stderr_fd].is_blocked = val;
|
||||
}
|
||||
|
||||
void handle_server_data()
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
read_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid,
|
||||
// s_hdr.len);
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_XON:
|
||||
set_blocked_outerr(s_hdr.clid, 0);
|
||||
break;
|
||||
case MSG_XOFF:
|
||||
set_blocked_outerr(s_hdr.clid, 1);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_EXEC_CMDLINE:
|
||||
handle_exec(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_JUST_EXEC:
|
||||
handle_just_exec(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_INPUT:
|
||||
handle_input(s_hdr.clid, s_hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_AGENT_CLIENT_END:
|
||||
remove_process(s_hdr.clid, -1);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "msg type from daemon is %d ?\n",
|
||||
s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_process_data(int fd)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
len = buffer_space_vchan_ext();
|
||||
if (len <= sizeof s_hdr)
|
||||
return;
|
||||
|
||||
ret = read(fd, buf, len - sizeof s_hdr);
|
||||
s_hdr.clid = process_fd[fd].clid;
|
||||
|
||||
if (process_fd[fd].type == FDTYPE_STDOUT)
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_STDOUT;
|
||||
else if (process_fd[fd].type == FDTYPE_STDERR)
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_STDERR;
|
||||
else {
|
||||
fprintf(stderr, "fd=%d, clid=%d, type=%d ?\n", fd,
|
||||
process_fd[fd].clid, process_fd[fd].type);
|
||||
exit(1);
|
||||
}
|
||||
s_hdr.len = ret;
|
||||
if (ret >= 0) {
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
write_all_vchan_ext(buf, ret);
|
||||
}
|
||||
if (ret == 0) {
|
||||
process_fd[fd].type = FDTYPE_INVALID;
|
||||
process_fd[fd].clid = -1;
|
||||
process_fd[fd].is_blocked = 0;
|
||||
close(fd);
|
||||
update_max_process_fd();
|
||||
}
|
||||
if (ret < 0)
|
||||
remove_process(process_fd[fd].clid, 127);
|
||||
}
|
||||
|
||||
volatile int child_exited;
|
||||
|
||||
void sigchld_handler(int x)
|
||||
{
|
||||
child_exited = 1;
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
int find_info(int pid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (client_info[i].pid == pid)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void handle_process_data_all(fd_set * select_fds)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& FD_ISSET(i, select_fds))
|
||||
handle_process_data(i);
|
||||
}
|
||||
|
||||
|
||||
void flush_out_err(int clid)
|
||||
{
|
||||
fd_set select_set;
|
||||
int fd_max = -1;
|
||||
int i;
|
||||
int ret;
|
||||
struct timeval tv;
|
||||
for (;;) {
|
||||
FD_ZERO(&select_set);
|
||||
for (i = 0; i <= max_process_fd; i++) {
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& !process_fd[i].is_blocked
|
||||
&& process_fd[i].clid == clid) {
|
||||
FD_SET(i, &select_set);
|
||||
fd_max = i;
|
||||
}
|
||||
}
|
||||
if (fd_max == -1)
|
||||
return;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
ret = select(fd_max + 1, &select_set, NULL, NULL, &tv);
|
||||
if (ret < 0 && errno != EINTR) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
}
|
||||
if (!ret)
|
||||
return;
|
||||
handle_process_data_all(&select_set);
|
||||
}
|
||||
}
|
||||
|
||||
void reap_children()
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
int clid;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
clid = find_info(pid);
|
||||
if (clid < 0)
|
||||
continue;
|
||||
flush_out_err(clid);
|
||||
remove_process(clid, status);
|
||||
}
|
||||
child_exited = 0;
|
||||
}
|
||||
|
||||
int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int max = -1;
|
||||
int fd, i;
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
|
||||
for (i = 0; i <= max_process_fd; i++)
|
||||
if (process_fd[i].type != FDTYPE_INVALID
|
||||
&& !process_fd[i].is_blocked) {
|
||||
FD_SET(i, rdset);
|
||||
max = i;
|
||||
}
|
||||
|
||||
FD_SET(trigger_fd, rdset);
|
||||
if (trigger_fd > max)
|
||||
max = trigger_fd;
|
||||
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (client_info[i].pid > 0 && client_info[i].is_blocked) {
|
||||
fd = client_info[i].stdin_fd;
|
||||
FD_SET(fd, wrset);
|
||||
if (fd > max)
|
||||
max = fd;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void flush_client_data_agent(int clid)
|
||||
{
|
||||
struct _client_info *info = &client_info[clid];
|
||||
switch (flush_client_data(info->stdin_fd, clid, &info->buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
info->is_blocked = 0;
|
||||
if (info->is_close_after_flush_needed) {
|
||||
close(info->stdin_fd);
|
||||
info->stdin_fd = -1;
|
||||
info->is_close_after_flush_needed = 0;
|
||||
}
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
remove_process(clid, 128);
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown flush_client_data?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_trigger_io()
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[5];
|
||||
int ret;
|
||||
|
||||
s_hdr.clid = 0;
|
||||
s_hdr.len = 0;
|
||||
if ((ret = read(trigger_fd, buf, 4)) == 4) {
|
||||
buf[4] = 0;
|
||||
if (!strcmp(buf, "FCPR"))
|
||||
s_hdr.clid = QREXEC_EXECUTE_FILE_COPY;
|
||||
else if (!strcmp(buf, "DVMR"))
|
||||
s_hdr.clid = QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM;
|
||||
if (s_hdr.clid) {
|
||||
s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_EXEC;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
}
|
||||
}
|
||||
// trigger_fd is nonblock - so no need to reopen
|
||||
// not really, need to reopen at EOF
|
||||
if (ret <= 0) {
|
||||
close(trigger_fd);
|
||||
trigger_fd =
|
||||
open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
int max;
|
||||
int i;
|
||||
|
||||
init();
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
|
||||
for (;;) {
|
||||
max = fill_fds_for_select(&rdset, &wrset);
|
||||
if (buffer_space_vchan_ext() <=
|
||||
sizeof(struct server_header))
|
||||
FD_ZERO(&rdset);
|
||||
|
||||
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
||||
|
||||
while (read_ready_vchan_ext())
|
||||
handle_server_data();
|
||||
|
||||
if (FD_ISSET(trigger_fd, &rdset))
|
||||
handle_trigger_io();
|
||||
|
||||
handle_process_data_all(&rdset);
|
||||
for (i = 0; i <= MAX_FDS; i++)
|
||||
if (client_info[i].pid > 0
|
||||
&& client_info[i].is_blocked
|
||||
&& FD_ISSET(client_info[i].stdin_fd, &wrset))
|
||||
flush_client_data_agent(i);
|
||||
|
||||
if (child_exited)
|
||||
reap_children();
|
||||
}
|
||||
}
|
255
qrexec/qrexec_client.c
Normal file
255
qrexec/qrexec_client.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <ioall.h>
|
||||
#include <sys/wait.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
int connect_unix_socket(char *domname)
|
||||
{
|
||||
int s, len;
|
||||
struct sockaddr_un remote;
|
||||
|
||||
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote.sun_family = AF_UNIX;
|
||||
snprintf(remote.sun_path, sizeof remote.sun_path,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname);
|
||||
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
|
||||
if (connect(s, (struct sockaddr *) &remote, len) == -1) {
|
||||
perror("connect");
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void do_exec(char *prog)
|
||||
{
|
||||
execl("/bin/bash", "bash", "-c", prog, NULL);
|
||||
}
|
||||
|
||||
int local_stdin_fd, local_stdout_fd;
|
||||
|
||||
void do_exit(int code)
|
||||
{
|
||||
int status;
|
||||
// sever communication lines; wait for child, if any
|
||||
// so that qrexec-daemon can count (recursively) spawned processes correctly
|
||||
close(local_stdin_fd);
|
||||
close(local_stdout_fd);
|
||||
waitpid(-1, &status, 0);
|
||||
exit(code);
|
||||
}
|
||||
|
||||
|
||||
void prepare_local_fds(char *cmdline)
|
||||
{
|
||||
int pid;
|
||||
if (!cmdline) {
|
||||
local_stdin_fd = 1;
|
||||
local_stdout_fd = 0;
|
||||
return;
|
||||
}
|
||||
do_fork_exec(cmdline, &pid, &local_stdin_fd, &local_stdout_fd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
void send_cmdline(int s, int type, char *cmdline)
|
||||
{
|
||||
struct client_header hdr;
|
||||
hdr.type = type;
|
||||
hdr.len = strlen(cmdline) + 1;
|
||||
if (!write_all(s, &hdr, sizeof(hdr))
|
||||
|| !write_all(s, cmdline, hdr.len)) {
|
||||
perror("write daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_input(int s)
|
||||
{
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int ret;
|
||||
ret = read(local_stdout_fd, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
do_exit(1);
|
||||
}
|
||||
if (ret == 0) {
|
||||
local_stdout_fd = -1;
|
||||
shutdown(s, SHUT_WR);
|
||||
}
|
||||
if (!write_all(s, buf, ret)) {
|
||||
perror("write daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_daemon_data(int s)
|
||||
{
|
||||
int status;
|
||||
struct client_header hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
|
||||
if (!read_all(s, &hdr, sizeof hdr)) {
|
||||
perror("read daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
if (hdr.len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "client_header.len=%d\n", hdr.len);
|
||||
do_exit(1);
|
||||
}
|
||||
if (!read_all(s, buf, hdr.len)) {
|
||||
perror("read daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
|
||||
switch (hdr.type) {
|
||||
case MSG_SERVER_TO_CLIENT_STDOUT:
|
||||
if (hdr.len == 0)
|
||||
close(local_stdin_fd);
|
||||
else if (!write_all(local_stdin_fd, buf, hdr.len)) {
|
||||
perror("write local stdout");
|
||||
do_exit(1);
|
||||
}
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_STDERR:
|
||||
write_all(2, buf, hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_EXIT_CODE:
|
||||
status = *(unsigned int *) buf;
|
||||
if (WIFEXITED(status))
|
||||
do_exit(WEXITSTATUS(status));
|
||||
else
|
||||
do_exit(255);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown msg %d\n", hdr.type);
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// perhaps we could save a syscall if we include both sides in both
|
||||
// rdset and wrset; to be investigated
|
||||
void handle_daemon_only_until_writable(s)
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
|
||||
do {
|
||||
FD_ZERO(&rdset);
|
||||
FD_ZERO(&wrset);
|
||||
FD_SET(s, &rdset);
|
||||
FD_SET(s, &wrset);
|
||||
|
||||
if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) {
|
||||
perror("select");
|
||||
do_exit(1);
|
||||
}
|
||||
if (FD_ISSET(s, &rdset))
|
||||
handle_daemon_data(s);
|
||||
} while (!FD_ISSET(s, &wrset));
|
||||
}
|
||||
|
||||
void select_loop(int s)
|
||||
{
|
||||
fd_set select_set;
|
||||
int max;
|
||||
for (;;) {
|
||||
handle_daemon_only_until_writable(s);
|
||||
FD_ZERO(&select_set);
|
||||
FD_SET(s, &select_set);
|
||||
max = s;
|
||||
if (local_stdout_fd != -1) {
|
||||
FD_SET(local_stdout_fd, &select_set);
|
||||
if (s < local_stdout_fd)
|
||||
max = local_stdout_fd;
|
||||
}
|
||||
if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) {
|
||||
perror("select");
|
||||
do_exit(1);
|
||||
}
|
||||
if (FD_ISSET(s, &select_set))
|
||||
handle_daemon_data(s);
|
||||
if (local_stdout_fd != -1
|
||||
&& FD_ISSET(local_stdout_fd, &select_set))
|
||||
handle_input(s);
|
||||
}
|
||||
}
|
||||
|
||||
void usage(char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s -d domain_num [-l local_prog] -e remote_cmdline\n"
|
||||
"-e means exit after sending cmd\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
char *domname = NULL;
|
||||
int s;
|
||||
int just_exec = 0;
|
||||
char *local_cmdline = NULL;
|
||||
while ((opt = getopt(argc, argv, "d:l:e")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domname = strdup(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
local_cmdline = strdup(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
just_exec = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
if (optind >= argc || !domname)
|
||||
usage(argv[0]);
|
||||
|
||||
s = connect_unix_socket(domname);
|
||||
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
||||
prepare_local_fds(local_cmdline);
|
||||
|
||||
if (just_exec)
|
||||
send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||
argv[optind]);
|
||||
else {
|
||||
send_cmdline(s, MSG_CLIENT_TO_SERVER_EXEC_CMDLINE,
|
||||
argv[optind]);
|
||||
select_loop(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
452
qrexec/qrexec_daemon.c
Normal file
452
qrexec/qrexec_daemon.c
Normal file
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ioall.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
enum client_flags {
|
||||
CLIENT_INVALID = 0,
|
||||
CLIENT_CMDLINE = 1,
|
||||
CLIENT_DATA = 2,
|
||||
CLIENT_DONT_READ = 4,
|
||||
CLIENT_OUTQ_FULL = 8
|
||||
};
|
||||
|
||||
struct _client {
|
||||
int state;
|
||||
struct buffer buffer;
|
||||
};
|
||||
|
||||
struct _client clients[MAX_FDS];
|
||||
|
||||
int max_client_fd = -1;
|
||||
int server_fd;
|
||||
|
||||
void handle_usr1(int x)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void sigchld_handler(int x);
|
||||
|
||||
char *remote_domain_name;
|
||||
|
||||
void init(int xid)
|
||||
{
|
||||
char dbg_log[256];
|
||||
int logfd;
|
||||
|
||||
if (xid <= 0) {
|
||||
fprintf(stderr, "domain id=0?\n");
|
||||
exit(1);
|
||||
}
|
||||
signal(SIGUSR1, handle_usr1);
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
pause();
|
||||
exit(0);
|
||||
}
|
||||
close(0);
|
||||
snprintf(dbg_log, sizeof(dbg_log),
|
||||
"/var/log/qubes/qrexec.%d.log", xid);
|
||||
umask(0007);
|
||||
logfd = open(dbg_log, O_WRONLY | O_CREAT | O_TRUNC, 0640);
|
||||
umask(0077);
|
||||
|
||||
dup2(logfd, 1);
|
||||
dup2(logfd, 2);
|
||||
|
||||
chdir("/var/run/qubes");
|
||||
if (setsid() < 0) {
|
||||
perror("setsid()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
remote_domain_name = peer_client_init(xid, REXEC_PORT);
|
||||
setuid(getuid());
|
||||
server_fd = get_server_socket(xid, remote_domain_name);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
kill(getppid(), SIGUSR1);
|
||||
}
|
||||
|
||||
void handle_new_client()
|
||||
{
|
||||
int fd = do_accept(server_fd);
|
||||
if (fd >= MAX_FDS) {
|
||||
fprintf(stderr, "too many clients ?\n");
|
||||
exit(1);
|
||||
}
|
||||
clients[fd].state = CLIENT_CMDLINE;
|
||||
buffer_init(&clients[fd].buffer);
|
||||
if (fd > max_client_fd)
|
||||
max_client_fd = fd;
|
||||
}
|
||||
|
||||
int children_count;
|
||||
|
||||
void flush_client(int fd)
|
||||
{
|
||||
int i;
|
||||
struct server_header s_hdr;
|
||||
|
||||
if (fork_and_flush_stdin(fd, &clients[fd].buffer))
|
||||
children_count++;
|
||||
close(fd);
|
||||
clients[fd].state = CLIENT_INVALID;
|
||||
buffer_free(&clients[fd].buffer);
|
||||
if (max_client_fd == fd) {
|
||||
for (i = fd; clients[i].state == CLIENT_INVALID && i >= 0;
|
||||
i--);
|
||||
max_client_fd = i;
|
||||
}
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_CLIENT_END;
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
}
|
||||
|
||||
void pass_to_agent(int fd, struct server_header *s_hdr)
|
||||
{
|
||||
int len = s_hdr->len;
|
||||
char buf[len];
|
||||
if (!read_all(fd, buf, len)) {
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
write_all_vchan_ext(s_hdr, sizeof(*s_hdr));
|
||||
write_all_vchan_ext(buf, len);
|
||||
}
|
||||
|
||||
void handle_client_cmdline(int fd)
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr;
|
||||
if (!read_all(fd, &hdr, sizeof hdr)) {
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
switch (hdr.type) {
|
||||
case MSG_CLIENT_TO_SERVER_EXEC_CMDLINE:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_EXEC_CMDLINE;
|
||||
break;
|
||||
case MSG_CLIENT_TO_SERVER_JUST_EXEC:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_JUST_EXEC;
|
||||
break;
|
||||
default:
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = hdr.len;
|
||||
pass_to_agent(fd, &s_hdr);
|
||||
clients[fd].state = CLIENT_DATA;
|
||||
set_nonblock(fd);
|
||||
if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC)
|
||||
flush_client(fd);
|
||||
|
||||
}
|
||||
|
||||
void handle_client_data(int fd)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int len, ret;
|
||||
|
||||
if (clients[fd].state == CLIENT_CMDLINE) {
|
||||
handle_client_cmdline(fd);
|
||||
return;
|
||||
}
|
||||
len = buffer_space_vchan_ext();
|
||||
if (len <= sizeof s_hdr)
|
||||
return;
|
||||
ret = read(fd, buf, len - sizeof(s_hdr));
|
||||
if (ret < 0) {
|
||||
perror("read client");
|
||||
flush_client(fd);
|
||||
return;
|
||||
}
|
||||
s_hdr.clid = fd;
|
||||
s_hdr.len = ret;
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_INPUT;
|
||||
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
write_all_vchan_ext(buf, ret);
|
||||
if (ret == 0)
|
||||
clients[fd].state |= CLIENT_DONT_READ;
|
||||
}
|
||||
|
||||
void flush_client_data_daemon(int clid)
|
||||
{
|
||||
switch (flush_client_data(clid, clid, &clients[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
clients[clid].state &= ~CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
flush_client(clid);
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown flush_client_data?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void pass_to_client(int clid, struct client_header *hdr)
|
||||
{
|
||||
int len = hdr->len;
|
||||
char buf[sizeof(*hdr) + len];
|
||||
|
||||
*(struct client_header *) buf = *hdr;
|
||||
read_all_vchan_ext(buf + sizeof(*hdr), len);
|
||||
|
||||
switch (write_stdin
|
||||
(clid, clid, buf, len + sizeof(*hdr),
|
||||
&clients[clid].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED:
|
||||
clients[clid].state |= CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
flush_client(clid);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown write_stdin?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int child_exited;
|
||||
|
||||
void sigchld_handler(int x)
|
||||
{
|
||||
child_exited = 1;
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
void reap_children()
|
||||
{
|
||||
int status;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0)
|
||||
children_count--;
|
||||
child_exited = 0;
|
||||
}
|
||||
|
||||
void wait_for_child()
|
||||
{
|
||||
int status;
|
||||
waitpid(-1, &status, 0);
|
||||
children_count--;
|
||||
}
|
||||
|
||||
#define MAX_CHILDREN 10
|
||||
void check_children_count()
|
||||
{
|
||||
if (children_count > MAX_CHILDREN) {
|
||||
fprintf(stderr,
|
||||
"max number of children reached, waiting for child exit...\n");
|
||||
wait_for_child();
|
||||
fprintf(stderr, "now children_count=%d, continuing.\n",
|
||||
children_count);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_trigger_exec(int req)
|
||||
{
|
||||
char *rcmd = NULL, *lcmd = NULL;
|
||||
int i;
|
||||
|
||||
check_children_count();
|
||||
switch (req) {
|
||||
case QREXEC_EXECUTE_FILE_COPY:
|
||||
rcmd = "directly:user:/usr/lib/qubes/qfile-agent";
|
||||
lcmd = "/usr/lib/qubes/qfile-daemon";
|
||||
break;
|
||||
case QREXEC_EXECUTE_FILE_COPY_FOR_DISPVM:
|
||||
rcmd = "directly:user:/usr/lib/qubes/qfile-agent-dvm";
|
||||
lcmd = "/usr/lib/qubes/qfile-daemon-dvm";
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "got trigger exec no %d\n", req);
|
||||
exit(1);
|
||||
}
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
children_count++;
|
||||
return;
|
||||
}
|
||||
for (i = 3; i < 256; i++)
|
||||
close(i);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
execl("/usr/lib/qubes/qrexec_client", "qrexec_client", "-d",
|
||||
remote_domain_name, "-l", lcmd, rcmd, NULL);
|
||||
perror("execl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void handle_agent_data()
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr;
|
||||
read_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.clid,
|
||||
// s_hdr.len);
|
||||
|
||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_EXEC) {
|
||||
handle_trigger_exec(s_hdr.clid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_hdr.clid >= MAX_FDS || s_hdr.clid < 0) {
|
||||
fprintf(stderr, "from agent: clid=%d\n", s_hdr.clid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (s_hdr.type == MSG_XOFF) {
|
||||
clients[s_hdr.clid].state |= CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
if (s_hdr.type == MSG_XON) {
|
||||
clients[s_hdr.clid].state &= ~CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_AGENT_TO_SERVER_STDOUT:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDOUT;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_STDERR:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDERR;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_EXIT_CODE:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_EXIT_CODE;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "from agent: type=%d\n", s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
hdr.len = s_hdr.len;
|
||||
if (hdr.len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "agent feeded %d of data bytes?\n",
|
||||
hdr.len);
|
||||
exit(1);
|
||||
}
|
||||
if (clients[s_hdr.clid].state == CLIENT_INVALID) {
|
||||
// benefit of doubt - maybe client exited earlier
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
read_all_vchan_ext(buf, s_hdr.len);
|
||||
return;
|
||||
}
|
||||
pass_to_client(s_hdr.clid, &hdr);
|
||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_EXIT_CODE)
|
||||
flush_client(s_hdr.clid);
|
||||
}
|
||||
|
||||
int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int i;
|
||||
int max = -1;
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
for (i = 0; i <= max_client_fd; i++) {
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& !(clients[i].state & CLIENT_DONT_READ)) {
|
||||
FD_SET(i, rdset);
|
||||
max = i;
|
||||
}
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& clients[i].state & CLIENT_OUTQ_FULL) {
|
||||
FD_SET(i, wrset);
|
||||
max = i;
|
||||
}
|
||||
}
|
||||
FD_SET(server_fd, rdset);
|
||||
if (server_fd > max)
|
||||
max = server_fd;
|
||||
return max;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
int i;
|
||||
int max;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s domainid\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
init(atoi(argv[1]));
|
||||
for (;;) {
|
||||
max = fill_fds_for_select(&rdset, &wrset);
|
||||
if (buffer_space_vchan_ext() <=
|
||||
sizeof(struct server_header))
|
||||
FD_ZERO(&rdset);
|
||||
|
||||
wait_for_vchan_or_argfd(max, &rdset, &wrset);
|
||||
|
||||
if (FD_ISSET(server_fd, &rdset))
|
||||
handle_new_client();
|
||||
|
||||
while (read_ready_vchan_ext())
|
||||
handle_agent_data();
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &rdset))
|
||||
handle_client_data(i);
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &wrset))
|
||||
flush_client_data_daemon(i);
|
||||
if (child_exited)
|
||||
reap_children();
|
||||
|
||||
}
|
||||
}
|
207
qrexec/txrx-vchan.c
Normal file
207
qrexec/txrx-vchan.c
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libvchan.h>
|
||||
#include <xs.h>
|
||||
#include <xenctrl.h>
|
||||
|
||||
static struct libvchan *ctrl;
|
||||
static int is_server;
|
||||
int write_all_vchan_ext(void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
|
||||
while (written < size) {
|
||||
ret =
|
||||
libvchan_write(ctrl, (char *) buf + written,
|
||||
size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "sent %d bytes\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
int read_all_vchan_ext(void *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret =
|
||||
libvchan_read(ctrl, (char *) buf + written,
|
||||
size - written);
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "EOF\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
// fprintf(stderr, "read %d bytes\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
int read_ready_vchan_ext()
|
||||
{
|
||||
return libvchan_data_ready(ctrl);
|
||||
}
|
||||
|
||||
int buffer_space_vchan_ext()
|
||||
{
|
||||
return libvchan_buffer_space(ctrl);
|
||||
}
|
||||
|
||||
// if the remote domain is destroyed, we get no notification
|
||||
// thus, we check for the status periodically
|
||||
|
||||
static int xc_handle = -1;
|
||||
void slow_check_for_libvchan_is_eof(struct libvchan *ctrl)
|
||||
{
|
||||
struct evtchn_status evst;
|
||||
evst.port = ctrl->evport;
|
||||
evst.dom = DOMID_SELF;
|
||||
if (xc_evtchn_status(xc_handle, &evst)) {
|
||||
perror("xc_evtchn_status");
|
||||
exit(1);
|
||||
}
|
||||
if (evst.status != EVTCHNSTAT_interdomain) {
|
||||
fprintf(stderr, "event channel disconnected\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int wait_for_vchan_or_argfd_once(int max, fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int vfd, ret;
|
||||
struct timeval tv = { 1, 100000 };
|
||||
vfd = libvchan_fd_for_select(ctrl);
|
||||
FD_SET(vfd, rdset);
|
||||
if (vfd > max)
|
||||
max = vfd;
|
||||
max++;
|
||||
ret = select(max, rdset, wrset, NULL, &tv);
|
||||
if (ret < 0) {
|
||||
if (errno != EINTR) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
} else {
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
fprintf(stderr, "eintr\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
if (libvchan_is_eof(ctrl)) {
|
||||
fprintf(stderr, "libvchan_is_eof\n");
|
||||
exit(0);
|
||||
}
|
||||
if (!is_server && ret == 0)
|
||||
slow_check_for_libvchan_is_eof(ctrl);
|
||||
if (FD_ISSET(vfd, rdset))
|
||||
// the following will never block; we need to do this to
|
||||
// clear libvchan_fd pending state
|
||||
libvchan_wait(ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
fd_set r = *rdset, w = *wrset;
|
||||
do {
|
||||
*rdset = r;
|
||||
*wrset = w;
|
||||
}
|
||||
while (wait_for_vchan_or_argfd_once(max, rdset, wrset) == 0);
|
||||
}
|
||||
|
||||
int peer_server_init(int port)
|
||||
{
|
||||
is_server = 1;
|
||||
ctrl = libvchan_server_init(port);
|
||||
if (!ctrl) {
|
||||
perror("libvchan_server_init");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *peer_client_init(int dom, int port)
|
||||
{
|
||||
struct xs_handle *xs;
|
||||
char buf[64];
|
||||
char *name;
|
||||
char *dummy;
|
||||
unsigned int len = 0;
|
||||
char devbuf[128];
|
||||
unsigned int count;
|
||||
char **vec;
|
||||
|
||||
// double_buffered = 1; // writes to vchan are buffered, nonblocking
|
||||
// double_buffer_init();
|
||||
xs = xs_daemon_open();
|
||||
if (!xs) {
|
||||
perror("xs_daemon_open");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "/local/domain/%d/name", dom);
|
||||
name = xs_read(xs, 0, buf, &len);
|
||||
if (!name) {
|
||||
perror("xs_read domainname");
|
||||
exit(1);
|
||||
}
|
||||
snprintf(devbuf, sizeof(devbuf),
|
||||
"/local/domain/%d/device/vchan/%d/event-channel", dom,
|
||||
port);
|
||||
xs_watch(xs, devbuf, devbuf);
|
||||
do {
|
||||
vec = xs_read_watch(xs, &count);
|
||||
if (vec)
|
||||
free(vec);
|
||||
len = 0;
|
||||
dummy = xs_read(xs, 0, devbuf, &len);
|
||||
}
|
||||
while (!dummy || !len); // wait for the server to create xenstore entries
|
||||
free(dummy);
|
||||
xs_daemon_close(xs);
|
||||
|
||||
// now client init should succeed; "while" is redundant
|
||||
while (!(ctrl = libvchan_client_init(dom, port)));
|
||||
|
||||
xc_handle = xc_interface_open();
|
||||
if (xc_handle < 0) {
|
||||
perror("xc_interface_open");
|
||||
exit(1);
|
||||
}
|
||||
return name;
|
||||
}
|
76
qrexec/unix_server.c
Normal file
76
qrexec/unix_server.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "qrexec.h"
|
||||
|
||||
int get_server_socket(int domid, char *domname)
|
||||
{
|
||||
struct sockaddr_un sockname;
|
||||
int s;
|
||||
char socket_address[40];
|
||||
char link_to_socket_name[strlen(domname) + sizeof(socket_address)];
|
||||
|
||||
snprintf(socket_address, sizeof(socket_address),
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid);
|
||||
snprintf(link_to_socket_name, sizeof link_to_socket_name,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname);
|
||||
unlink(socket_address);
|
||||
unlink(link_to_socket_name);
|
||||
symlink(socket_address, link_to_socket_name);
|
||||
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
memset(&sockname, 0, sizeof(sockname));
|
||||
sockname.sun_family = AF_UNIX;
|
||||
memcpy(sockname.sun_path, socket_address, strlen(socket_address));
|
||||
|
||||
if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
// chmod(sockname.sun_path, 0666);
|
||||
if (listen(s, 5) == -1) {
|
||||
perror("listen() failed\n");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int do_accept(int s)
|
||||
{
|
||||
struct sockaddr_un peer;
|
||||
unsigned int addrlen;
|
||||
int fd;
|
||||
addrlen = sizeof(peer);
|
||||
fd = accept(s, (struct sockaddr *) &peer, &addrlen);
|
||||
if (fd == -1) {
|
||||
perror("unix accept");
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
131
qrexec/write_stdin.c
Normal file
131
qrexec/write_stdin.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <ioall.h>
|
||||
#include <stdlib.h>
|
||||
#include "qrexec.h"
|
||||
#include "buffer.h"
|
||||
#include "glue.h"
|
||||
|
||||
int flush_client_data(int fd, int clid, struct buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
int len;
|
||||
for (;;) {
|
||||
len = buffer_len(buffer);
|
||||
if (len > MAX_DATA_CHUNK)
|
||||
len = MAX_DATA_CHUNK;
|
||||
ret = write(fd, buffer_data(buffer), len);
|
||||
if (ret == -1) {
|
||||
if (errno != EAGAIN) {
|
||||
return WRITE_STDIN_ERROR;
|
||||
} else
|
||||
return WRITE_STDIN_BUFFERED;
|
||||
}
|
||||
buffer_remove(buffer, len);
|
||||
len = buffer_len(buffer);
|
||||
if (!len) {
|
||||
struct server_header s_hdr;
|
||||
s_hdr.type = MSG_XON;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
return WRITE_STDIN_OK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int write_stdin(int fd, int clid, char *data, int len,
|
||||
struct buffer *buffer)
|
||||
{
|
||||
int ret;
|
||||
int written = 0;
|
||||
|
||||
if (buffer_len(buffer)) {
|
||||
buffer_append(buffer, data, len);
|
||||
return WRITE_STDIN_BUFFERED;
|
||||
}
|
||||
while (written < len) {
|
||||
ret = write(fd, data + written, len - written);
|
||||
if (ret == 0) {
|
||||
perror("write_stdin: write returns 0 ???");
|
||||
exit(1);
|
||||
}
|
||||
if (ret == -1) {
|
||||
struct server_header s_hdr;
|
||||
|
||||
if (errno != EAGAIN)
|
||||
return WRITE_STDIN_ERROR;
|
||||
|
||||
buffer_append(buffer, data + written,
|
||||
len - written);
|
||||
|
||||
s_hdr.type = MSG_XOFF;
|
||||
s_hdr.clid = clid;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||
|
||||
return WRITE_STDIN_BUFFERED;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return WRITE_STDIN_OK;
|
||||
|
||||
}
|
||||
|
||||
void set_nonblock(int fd)
|
||||
{
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
}
|
||||
|
||||
void set_block(int fd)
|
||||
{
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
|
||||
}
|
||||
|
||||
int fork_and_flush_stdin(int fd, struct buffer *buffer)
|
||||
{
|
||||
int i;
|
||||
if (!buffer_len(buffer))
|
||||
return 0;
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (i != fd && i != 2)
|
||||
close(i);
|
||||
set_block(fd);
|
||||
write_all(fd, buffer_data(buffer), buffer_len(buffer));
|
||||
exit(0);
|
||||
}
|
@ -60,6 +60,9 @@ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
|
||||
%build
|
||||
make clean all
|
||||
make -C ../common
|
||||
make -C ../vchan
|
||||
make -C ../u2mfn
|
||||
make -C ../qrexec
|
||||
|
||||
%install
|
||||
|
||||
@ -67,9 +70,13 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d
|
||||
cp qubes_core_appvm $RPM_BUILD_ROOT/etc/init.d/
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/bin
|
||||
cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin
|
||||
cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm qvm-open-in-dvm2 $RPM_BUILD_ROOT/usr/bin
|
||||
cp qvm-copy-to-vm2 $RPM_BUILD_ROOT/usr/bin
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp qvm-copy-to-vm2.kde $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp dvm_file_editor qfile-agent qfile-agent-dvm qfile-unpacker $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
ln -s /usr/bin/qvm-open-in-dvm $RPM_BUILD_ROOT/usr/lib/qubes/qvm-dvm-transfer
|
||||
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir}
|
||||
@ -86,6 +93,18 @@ cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11
|
||||
mkdir -p $RPM_BUILD_ROOT/home_volatile/user
|
||||
chown 500:500 $RPM_BUILD_ROOT/home_volatile/user
|
||||
|
||||
install -D ../vchan/libvchan.h $RPM_BUILD_ROOT/usr/include/libvchan.h
|
||||
install -D ../u2mfn/u2mfnlib.h $RPM_BUILD_ROOT/usr/include/u2mfnlib.h
|
||||
install -D ../u2mfn/u2mfn-kernel.h $RPM_BUILD_ROOT/usr/include/u2mfn-kernel.h
|
||||
|
||||
install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so
|
||||
install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/var/run/qubes
|
||||
|
||||
%triggerin -- initscripts
|
||||
cp /var/lib/qubes/serial.conf /etc/init/serial.conf
|
||||
|
||||
%post
|
||||
|
||||
chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!"
|
||||
@ -111,14 +130,22 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%defattr(-,root,root,-)
|
||||
/etc/init.d/qubes_core_appvm
|
||||
/usr/bin/qvm-copy-to-vm
|
||||
/usr/bin/qvm-copy-to-vm2
|
||||
/usr/lib/qubes/qvm-copy-to-vm.kde
|
||||
/usr/lib/qubes/qvm-copy-to-vm2.kde
|
||||
%attr(4755,root,root) /usr/bin/qvm-open-in-dvm
|
||||
/usr/bin/qvm-open-in-dvm2
|
||||
/usr/lib/qubes/qvm-dvm-transfer
|
||||
/usr/lib/qubes/meminfo-writer
|
||||
/usr/lib/qubes/dvm_file_editor
|
||||
%{kde_service_dir}/qvm-copy.desktop
|
||||
%{kde_service_dir}/qvm-dvm.desktop
|
||||
%attr(4755,root,root) /usr/lib/qubes/qubes_penctl
|
||||
/usr/lib/qubes/qubes_add_pendrive_script
|
||||
/usr/lib/qubes/qrexec_agent
|
||||
/usr/lib/qubes/qfile-agent
|
||||
/usr/lib/qubes/qfile-agent-dvm
|
||||
/usr/lib/qubes/qfile-unpacker
|
||||
/etc/udev/rules.d/qubes.rules
|
||||
%dir /mnt/incoming
|
||||
%dir /mnt/outgoing
|
||||
@ -127,3 +154,20 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%dir /home_volatile
|
||||
%attr(700,user,user) /home_volatile/user
|
||||
/etc/X11/xorg-preload-apps.conf
|
||||
/usr/include/libvchan.h
|
||||
%{_libdir}/libvchan.so
|
||||
%{_libdir}/libu2mfn.so
|
||||
%dir /var/run/qubes
|
||||
|
||||
|
||||
%package devel
|
||||
Summary: Include files for qubes core libraries
|
||||
License: GPL v2 only
|
||||
Group: Development/Sources
|
||||
|
||||
%description devel
|
||||
|
||||
%files devel
|
||||
/usr/include/libvchan.h
|
||||
/usr/include/u2mfnlib.h
|
||||
/usr/include/u2mfn-kernel.h
|
||||
|
@ -51,6 +51,9 @@ python -m compileall qvm-core qmemman
|
||||
python -O -m compileall qvm-core qmemman
|
||||
make -C restore
|
||||
make -C ../common
|
||||
make -C ../qrexec
|
||||
make -C ../vchan
|
||||
make -C ../u2mfn
|
||||
|
||||
%install
|
||||
|
||||
@ -90,10 +93,14 @@ cp aux-tools/reset_vm_configs.py $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp pendrive_swapper/qubes_pencmd $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp qmemman/server.py $RPM_BUILD_ROOT/usr/lib/qubes/qmemman_daemon.py
|
||||
cp ../common/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp ../qrexec/qrexec_daemon $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp ../qrexec/qrexec_client $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
|
||||
cp restore/xenstore-watch restore/qvm-create-default-dvm $RPM_BUILD_ROOT/usr/bin
|
||||
cp restore/qubes_restore restore/xenfreepages $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp restore/qfile-daemon-dvm $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
cp restore/qfile-daemon $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates
|
||||
@ -127,6 +134,9 @@ cp pm-utils/02qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
mkdir -p $RPM_BUILD_ROOT/var/log/qubes
|
||||
mkdir -p $RPM_BUILD_ROOT/var/run/qubes
|
||||
|
||||
install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so
|
||||
install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so
|
||||
|
||||
%post
|
||||
|
||||
# Create NetworkManager configuration if we do not have it
|
||||
@ -284,6 +294,8 @@ fi
|
||||
/usr/lib/qubes/qubes_pencmd
|
||||
/usr/lib/qubes/qmemman_daemon.py*
|
||||
/usr/lib/qubes/meminfo-writer
|
||||
/usr/lib/qubes/qfile-daemon-dvm*
|
||||
/usr/lib/qubes/qfile-daemon
|
||||
%attr(770,root,qubes) %dir /var/lib/qubes
|
||||
%attr(770,root,qubes) %dir /var/lib/qubes/vm-templates
|
||||
%attr(770,root,qubes) %dir /var/lib/qubes/appvms
|
||||
@ -307,6 +319,10 @@ fi
|
||||
/etc/xen/scripts/block-snapshot
|
||||
/etc/xen/scripts/block-origin
|
||||
/etc/xen/scripts/vif-route-qubes
|
||||
/usr/lib/qubes/qrexec_client
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/qrexec_daemon
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/xenfreepages
|
||||
%attr(2770,root,qubes) %dir /var/log/qubes
|
||||
%attr(770,root,qubes) %dir /var/run/qubes
|
||||
%{_libdir}/libvchan.so
|
||||
%{_libdir}/libu2mfn.so
|
||||
|
@ -45,6 +45,8 @@ The Qubes core files for installation inside a Qubes NetVM.
|
||||
%pre
|
||||
|
||||
%build
|
||||
make -C ../vchan
|
||||
make -C ../u2mfn
|
||||
|
||||
%install
|
||||
|
||||
@ -89,4 +91,3 @@ rm -rf $RPM_BUILD_ROOT
|
||||
/etc/NetworkManager/dispatcher.d/qubes_nmhook
|
||||
/etc/NetworkManager/dispatcher.d/30-qubes_external_ip
|
||||
/etc/xen/scripts/vif-route-qubes
|
||||
%dir /var/run/qubes
|
||||
|
33
u2mfn/Makefile
Normal file
33
u2mfn/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-g -Wall
|
||||
all: libu2mfn.so
|
||||
|
||||
libu2mfn.so : u2mfnlib.o
|
||||
gcc -shared -o libu2mfn.so u2mfnlib.o
|
||||
u2mfnlib.o: u2mfnlib.c
|
||||
gcc -fPIC -Wall -g -c u2mfnlib.c
|
||||
clean:
|
||||
rm -f *.o *so *~ libu2mfn.so
|
||||
|
||||
|
26
u2mfn/u2mfn-kernel.h
Normal file
26
u2mfn/u2mfn-kernel.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define U2MFN_MAGIC 0xf5 // See ioctl-number.txt in kernel docs
|
||||
|
||||
#define U2MFN_GET_MFN_FOR_PAGE _IOW (U2MFN_MAGIC, 1, int)
|
||||
#define U2MFN_GET_LAST_MFN _IO (U2MFN_MAGIC, 2)
|
75
u2mfn/u2mfnlib.c
Normal file
75
u2mfn/u2mfnlib.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "u2mfn-kernel.h"
|
||||
|
||||
|
||||
static int u2mfn_fd = -1;
|
||||
|
||||
static int get_fd()
|
||||
{
|
||||
if (u2mfn_fd == -1) {
|
||||
u2mfn_fd = open("/proc/u2mfn", O_RDWR);
|
||||
if (u2mfn_fd < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int u2mfn_get_mfn_for_page(long va, int *mfn)
|
||||
{
|
||||
if (get_fd())
|
||||
return -1;
|
||||
*mfn = ioctl(u2mfn_fd, U2MFN_GET_MFN_FOR_PAGE, va);
|
||||
if (*mfn == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int u2mfn_get_last_mfn(int *mfn)
|
||||
{
|
||||
if (get_fd())
|
||||
return -1;
|
||||
|
||||
*mfn = ioctl(u2mfn_fd, U2MFN_GET_LAST_MFN, 0);
|
||||
if (*mfn == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *u2mfn_alloc_kpage()
|
||||
{
|
||||
char *ret;
|
||||
if (get_fd())
|
||||
return MAP_FAILED;
|
||||
ret =
|
||||
mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, u2mfn_fd, 0);
|
||||
return ret;
|
||||
}
|
24
u2mfn/u2mfnlib.h
Normal file
24
u2mfn/u2mfnlib.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
int u2mfn_get_mfn_for_page(long va, int *mfn) ;
|
||||
int u2mfn_get_last_mfn(int *mfn) ;
|
||||
char *u2mfn_alloc_kpage(void) ;
|
39
vchan/Makefile
Normal file
39
vchan/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-g -Wall -I../u2mfn
|
||||
all: libvchan.so
|
||||
|
||||
libvchan.so : init.o io.o
|
||||
gcc -shared -o libvchan.so init.o io.o -L ../u2mfn -lu2mfn
|
||||
init.o: init.c
|
||||
gcc -fPIC -Wall -g -c init.c
|
||||
io.o: io.c
|
||||
gcc -fPIC -Wall -g -c io.c
|
||||
node: node.o libvchan.so
|
||||
gcc -g -o node node.o -L. -lvchan -lxenctrl -lxenstore
|
||||
node-select: node-select.o libvchan.so
|
||||
gcc -g -o node-select node-select.o -L. -lvchan -lxenctrl -lxenstore
|
||||
clean:
|
||||
rm -f *.o *so *~ client server node node-select
|
||||
|
||||
|
224
vchan/init.c
Normal file
224
vchan/init.c
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <malloc.h>
|
||||
#include <xs.h>
|
||||
#include <string.h>
|
||||
#include <xenctrl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "libvchan.h"
|
||||
#include "../u2mfn/u2mfnlib.h"
|
||||
|
||||
static int ring_init(struct libvchan *ctrl)
|
||||
{
|
||||
int u2mfn = open("/proc/u2mfn", O_RDONLY);
|
||||
int mfn;
|
||||
struct vchan_interface *ring;
|
||||
ring = (struct vchan_interface *) u2mfn_alloc_kpage ();
|
||||
|
||||
if (ring == MAP_FAILED)
|
||||
return -1;
|
||||
|
||||
ctrl->ring = ring;
|
||||
if (u2mfn_get_last_mfn (&mfn) < 0)
|
||||
return -1;
|
||||
|
||||
ctrl->ring_ref = mfn;
|
||||
close(u2mfn);
|
||||
ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out =
|
||||
0;
|
||||
ring->server_closed = ring->client_closed = 0;
|
||||
ring->debug = 0xaabbccdd;
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
creates event channel;
|
||||
creates "ring-ref" and "event-channel" xenstore entries;
|
||||
waits for connection to event channel from the peer
|
||||
*/
|
||||
static int server_interface_init(struct libvchan *ctrl, int devno)
|
||||
{
|
||||
int ret = -1;
|
||||
struct xs_handle *xs;
|
||||
char buf[64];
|
||||
char ref[16];
|
||||
int evfd;
|
||||
evtchn_port_or_error_t port;
|
||||
xs = xs_domain_open();
|
||||
if (!xs) {
|
||||
return ret;
|
||||
}
|
||||
evfd = xc_evtchn_open();
|
||||
if (evfd < 0)
|
||||
goto fail;
|
||||
ctrl->evfd = evfd;
|
||||
// the following hardcoded 0 is the peer domain id
|
||||
port = xc_evtchn_bind_unbound_port(evfd, 0);
|
||||
if (port < 0)
|
||||
goto fail2;
|
||||
ctrl->evport = port;
|
||||
snprintf(ref, sizeof ref, "%d", ctrl->ring_ref);
|
||||
snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno);
|
||||
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
||||
goto fail2;
|
||||
snprintf(ref, sizeof ref, "%d", ctrl->evport);
|
||||
snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno);
|
||||
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
||||
goto fail2;
|
||||
// wait for the peer to arrive
|
||||
if (xc_evtchn_pending(evfd) == -1)
|
||||
goto fail2;
|
||||
xc_evtchn_unmask(ctrl->evfd, ctrl->evport);
|
||||
snprintf(buf, sizeof buf, "device/vchan/%d", devno);
|
||||
xs_rm(xs, 0, buf);
|
||||
|
||||
ret = 0;
|
||||
fail2:
|
||||
if (ret)
|
||||
close(evfd);
|
||||
fail:
|
||||
xs_daemon_close(xs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define dir_select(dir1, dir2) \
|
||||
ctrl->wr_cons = &ctrl->ring->cons_##dir1; \
|
||||
ctrl->wr_prod = &ctrl->ring->prod_##dir1; \
|
||||
ctrl->rd_cons = &ctrl->ring->cons_##dir2; \
|
||||
ctrl->rd_prod = &ctrl->ring->prod_##dir2; \
|
||||
ctrl->wr_ring = ctrl->ring->buf_##dir1; \
|
||||
ctrl->rd_ring = ctrl->ring->buf_##dir2; \
|
||||
ctrl->wr_ring_size = sizeof(ctrl->ring->buf_##dir1); \
|
||||
ctrl->rd_ring_size = sizeof(ctrl->ring->buf_##dir2)
|
||||
|
||||
/**
|
||||
Run in AppVM (any domain).
|
||||
Sleeps until the connection is established.
|
||||
\param devno something like a well-known port.
|
||||
\returns NULL on failure, handle on success
|
||||
*/
|
||||
struct libvchan *libvchan_server_init(int devno)
|
||||
{
|
||||
struct libvchan *ctrl =
|
||||
(struct libvchan *) malloc(sizeof(struct libvchan));
|
||||
if (!ctrl)
|
||||
return 0;
|
||||
if (ring_init(ctrl))
|
||||
return 0;;
|
||||
if (server_interface_init(ctrl, devno))
|
||||
return 0;
|
||||
/*
|
||||
We want the same code for read/write functions, regardless whether
|
||||
we are client, or server. Thus, we do not access buf_in nor buf_out
|
||||
buffers directly. Instead, in *_init functions, the dir_select
|
||||
macro assigns proper values to wr* and rd* pointers, so that they
|
||||
point to correct one out of buf_in or buf_out related fields.
|
||||
*/
|
||||
dir_select(in, out);
|
||||
ctrl->is_server = 1;
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
/**
|
||||
retrieves ring-ref and event-channel numbers from xenstore (if
|
||||
they don't exist, return error, because nobody seems to listen);
|
||||
map the ring, connect the event channel
|
||||
*/
|
||||
static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
||||
{
|
||||
int ret = -1;
|
||||
unsigned int len;
|
||||
struct xs_handle *xs;
|
||||
int xcfd;
|
||||
char buf[64];
|
||||
char *ref;
|
||||
int evfd;
|
||||
int remote_port;
|
||||
xs = xs_daemon_open();
|
||||
if (!xs) {
|
||||
return ret;
|
||||
}
|
||||
snprintf(buf, sizeof buf,
|
||||
"/local/domain/%d/device/vchan/%d/ring-ref", domain,
|
||||
devno);
|
||||
ref = xs_read(xs, 0, buf, &len);
|
||||
if (!ref)
|
||||
goto fail;
|
||||
ctrl->ring_ref = atoi(ref);
|
||||
if (!ctrl->ring_ref)
|
||||
goto fail;
|
||||
free(ref);
|
||||
snprintf(buf, sizeof buf,
|
||||
"/local/domain/%d/device/vchan/%d/event-channel", domain,
|
||||
devno);
|
||||
ref = xs_read(xs, 0, buf, &len);
|
||||
if (!ref)
|
||||
goto fail;
|
||||
remote_port = atoi(ref);
|
||||
if (!remote_port)
|
||||
goto fail;
|
||||
free(ref);
|
||||
xcfd = xc_interface_open();
|
||||
if (xcfd < 0)
|
||||
goto fail;
|
||||
ctrl->ring = (struct vchan_interface *)
|
||||
xc_map_foreign_range(xcfd, domain, 4096,
|
||||
PROT_READ | PROT_WRITE, ctrl->ring_ref);
|
||||
close(xcfd);
|
||||
if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED)
|
||||
goto fail;
|
||||
evfd = xc_evtchn_open();
|
||||
if (evfd < 0)
|
||||
goto fail;
|
||||
ctrl->evfd = evfd;
|
||||
ctrl->evport =
|
||||
xc_evtchn_bind_interdomain(evfd, domain, remote_port);
|
||||
if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport))
|
||||
close(evfd);
|
||||
else
|
||||
ret = 0;
|
||||
fail:
|
||||
xs_daemon_close(xs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Run on the client side of connection (currently, must be dom0).
|
||||
\returns NULL on failure (e.g. noone listening), handle on success
|
||||
*/
|
||||
struct libvchan *libvchan_client_init(int domain, int devno)
|
||||
{
|
||||
struct libvchan *ctrl =
|
||||
(struct libvchan *) malloc(sizeof(struct libvchan));
|
||||
if (!ctrl)
|
||||
return 0;
|
||||
if (client_interface_init(ctrl, domain, devno))
|
||||
return 0;
|
||||
// See comment in libvchan_server_init
|
||||
dir_select(out, in);
|
||||
ctrl->is_server = 0;
|
||||
return ctrl;
|
||||
}
|
159
vchan/io.c
Normal file
159
vchan/io.c
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libvchan.h"
|
||||
#include <xenctrl.h>
|
||||
#include <string.h>
|
||||
/**
|
||||
\return How much data is immediately available for reading
|
||||
*/
|
||||
int libvchan_data_ready(struct libvchan *ctrl)
|
||||
{
|
||||
return *ctrl->rd_prod - *ctrl->rd_cons;
|
||||
}
|
||||
|
||||
/**
|
||||
\return How much space is available for writing, without blocking
|
||||
*/
|
||||
int libvchan_buffer_space(struct libvchan *ctrl)
|
||||
{
|
||||
return ctrl->wr_ring_size - (*ctrl->wr_prod - *ctrl->wr_cons);
|
||||
}
|
||||
|
||||
static int do_notify(struct libvchan *ctrl)
|
||||
{
|
||||
return xc_evtchn_notify(ctrl->evfd, ctrl->evport);
|
||||
}
|
||||
|
||||
/// returns nonzero if the peer has closed connection
|
||||
int libvchan_is_eof(struct libvchan *ctrl)
|
||||
{
|
||||
if (ctrl->is_server) {
|
||||
if (ctrl->ring->client_closed)
|
||||
return -1;
|
||||
} else {
|
||||
if (ctrl->ring->server_closed) {
|
||||
ctrl->ring->client_closed = 1;
|
||||
do_notify(ctrl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// waits for the peer to do any action
|
||||
/**
|
||||
\return -1 return value means peer has closed
|
||||
*/
|
||||
int libvchan_wait(struct libvchan *ctrl)
|
||||
{
|
||||
int ret;
|
||||
ret = xc_evtchn_pending(ctrl->evfd);
|
||||
if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport))
|
||||
return -1;
|
||||
if (ret!=-1 && libvchan_is_eof(ctrl))
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
may sleep (only if no buffer space available);
|
||||
may write less data than requested;
|
||||
returns the amount of data processed, -1 on error or peer close
|
||||
*/
|
||||
int libvchan_write(struct libvchan *ctrl, char *data, int size)
|
||||
{
|
||||
int avail, avail_contig;
|
||||
int real_idx;
|
||||
while ((avail = libvchan_buffer_space(ctrl)) == 0)
|
||||
if (libvchan_wait(ctrl) < 0)
|
||||
return -1;
|
||||
if (avail > size)
|
||||
avail = size;
|
||||
real_idx = (*ctrl->wr_prod) & (ctrl->wr_ring_size - 1);
|
||||
avail_contig = ctrl->wr_ring_size - real_idx;
|
||||
if (avail_contig < avail)
|
||||
avail = avail_contig;
|
||||
memcpy(ctrl->wr_ring + real_idx, data, avail);
|
||||
*ctrl->wr_prod += avail;
|
||||
if (do_notify(ctrl) < 0)
|
||||
return -1;
|
||||
return avail;
|
||||
}
|
||||
|
||||
/**
|
||||
may sleep (only if no data is available for reading);
|
||||
may return less data than requested;
|
||||
returns the amount of data processed, -1 on error or peer close
|
||||
*/
|
||||
int libvchan_read(struct libvchan *ctrl, char *data, int size)
|
||||
{
|
||||
int avail, avail_contig;
|
||||
int real_idx;
|
||||
while ((avail = libvchan_data_ready(ctrl)) == 0)
|
||||
if (libvchan_wait(ctrl) < 0)
|
||||
return -1;
|
||||
if (avail > size)
|
||||
avail = size;
|
||||
real_idx = (*ctrl->rd_cons) & (ctrl->rd_ring_size - 1);
|
||||
avail_contig = ctrl->rd_ring_size - real_idx;
|
||||
if (avail_contig < avail)
|
||||
avail = avail_contig;
|
||||
memcpy(data, ctrl->rd_ring + real_idx, avail);
|
||||
*ctrl->rd_cons += avail;
|
||||
if (do_notify(ctrl) < 0)
|
||||
return -1;
|
||||
return avail;
|
||||
}
|
||||
|
||||
/**
|
||||
Wait fot the writes to finish, then notify the peer of closing
|
||||
On server side, it waits for the peer to acknowledge
|
||||
*/
|
||||
int libvchan_close(struct libvchan *ctrl)
|
||||
{
|
||||
while (*ctrl->wr_prod != *ctrl->wr_cons)
|
||||
if (libvchan_wait(ctrl) < 0)
|
||||
return -1;
|
||||
if (ctrl->is_server) {
|
||||
ctrl->ring->server_closed = 1;
|
||||
do_notify(ctrl);
|
||||
while (!ctrl->ring->client_closed
|
||||
&& libvchan_wait(ctrl) == 0);
|
||||
} else {
|
||||
ctrl->ring->client_closed = 1;
|
||||
do_notify(ctrl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// The fd to use for select() set
|
||||
int libvchan_fd_for_select(struct libvchan *ctrl)
|
||||
{
|
||||
return ctrl->evfd;
|
||||
}
|
||||
|
||||
/// Unmasks event channel; must be called before calling select(), and only then
|
||||
void libvchan_prepare_to_select(struct libvchan *ctrl)
|
||||
{
|
||||
xc_evtchn_unmask(ctrl->evfd, ctrl->evport);
|
||||
}
|
60
vchan/libvchan.h
Normal file
60
vchan/libvchan.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint32_t VCHAN_RING_IDX;
|
||||
|
||||
/// struct vchan_interface is placed in memory shared between domains
|
||||
struct vchan_interface {
|
||||
// One buffer for each data direction
|
||||
char buf_in[1024];
|
||||
char buf_out[2048];
|
||||
// standard consumer/producer interface, one pair per buffer
|
||||
VCHAN_RING_IDX cons_in, prod_in, cons_out, prod_out;
|
||||
uint32_t debug;
|
||||
int client_closed, server_closed;
|
||||
};
|
||||
/// struct libvchan is a control structure, passed to all library calls
|
||||
struct libvchan {
|
||||
struct vchan_interface *ring;
|
||||
uint32_t ring_ref;
|
||||
/// descriptor to event channel interface
|
||||
int evfd;
|
||||
int evport;
|
||||
VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod;
|
||||
char *rd_ring, *wr_ring;
|
||||
int rd_ring_size, wr_ring_size;
|
||||
int is_server;
|
||||
};
|
||||
|
||||
struct libvchan *libvchan_server_init(int devno);
|
||||
|
||||
struct libvchan *libvchan_client_init(int domain, int devno);
|
||||
|
||||
int libvchan_write(struct libvchan *ctrl, char *data, int size);
|
||||
int libvchan_read(struct libvchan *ctrl, char *data, int size);
|
||||
int libvchan_wait(struct libvchan *ctrl);
|
||||
int libvchan_close(struct libvchan *ctrl);
|
||||
void libvchan_prepare_to_select(struct libvchan *ctrl);
|
||||
int libvchan_fd_for_select(struct libvchan *ctrl);
|
||||
int libvchan_is_eof(struct libvchan *ctrl);
|
||||
int libvchan_data_ready(struct libvchan *ctrl);
|
||||
int libvchan_buffer_space(struct libvchan *ctrl);
|
133
vchan/node-select.c
Normal file
133
vchan/node-select.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libvchan.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
int libvchan_write_all(struct libvchan *ctrl, char *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = libvchan_write(ctrl, buf + written, size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int write_all(int fd, char *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = write(fd, buf + written, size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "usage:\n\tnode-select server nodeid\n"
|
||||
"or\n" "\tnode-select client domainid nodeid\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define BUFSIZE 5000
|
||||
char buf[BUFSIZE];
|
||||
|
||||
/**
|
||||
Simple libvchan application, both client and server.
|
||||
Both sides may write and read, both from the libvchan and from
|
||||
stdin/stdout (just like netcat). More code is required to avoid
|
||||
deadlock when both sides write, and noone reads.
|
||||
*/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
int libvchan_fd;
|
||||
struct libvchan *ctrl = 0;
|
||||
if (argc < 3)
|
||||
usage();
|
||||
if (!strcmp(argv[1], "server"))
|
||||
ctrl = libvchan_server_init(atoi(argv[2]));
|
||||
else if (!strcmp(argv[1], "client"))
|
||||
ctrl = libvchan_client_init(atoi(argv[2]), atoi(argv[3]));
|
||||
else
|
||||
usage();
|
||||
if (!ctrl) {
|
||||
perror("libvchan_*_init");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
libvchan_fd = libvchan_fd_for_select(ctrl);
|
||||
for (;;) {
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(0, &rfds);
|
||||
FD_SET(libvchan_fd, &rfds);
|
||||
// libvchan_prepare_to_select(ctrl);
|
||||
ret = select(libvchan_fd + 1, &rfds, NULL, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
perror("select");
|
||||
exit(1);
|
||||
}
|
||||
if (libvchan_is_eof(ctrl))
|
||||
exit(0);
|
||||
if (FD_ISSET(libvchan_fd, &rfds))
|
||||
// we don't care about the result, but we need to do the read to
|
||||
// clear libvchan_fd pendind state
|
||||
libvchan_wait(ctrl);
|
||||
while (libvchan_data_ready(ctrl) > 0) {
|
||||
ret = libvchan_read(ctrl, buf, BUFSIZE);
|
||||
if (ret < 0)
|
||||
exit(0);
|
||||
write_all(1, buf, ret);
|
||||
}
|
||||
if (FD_ISSET(0, &rfds)) {
|
||||
ret = read(0, buf, BUFSIZE);
|
||||
if (ret == 0) {
|
||||
libvchan_close(ctrl);
|
||||
exit(0);
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("read 0");
|
||||
exit(1);
|
||||
}
|
||||
// libvchan_write_all can block; so if both sides write a lot,
|
||||
// we can deadlock. Need higher level solution; would libvchan_write be ok ?
|
||||
libvchan_write_all(ctrl, buf, ret);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
157
vchan/node.c
Normal file
157
vchan/node.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "libvchan.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
int libvchan_write_all(struct libvchan *ctrl, char *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = libvchan_write(ctrl, buf + written, size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int write_all(int fd, char *buf, int size)
|
||||
{
|
||||
int written = 0;
|
||||
int ret;
|
||||
while (written < size) {
|
||||
ret = write(fd, buf + written, size - written);
|
||||
if (ret <= 0) {
|
||||
perror("write");
|
||||
exit(1);
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
fprintf(stderr, "usage:\n\tnode server [read|write] nodeid\n"
|
||||
"or\n" "\tnode client [read|write] domainid nodeid\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define BUFSIZE 5000
|
||||
char buf[BUFSIZE];
|
||||
void reader(struct libvchan *ctrl)
|
||||
{
|
||||
int size;
|
||||
for (;;) {
|
||||
size = rand() % (BUFSIZE - 1) + 1;
|
||||
size = libvchan_read(ctrl, buf, size);
|
||||
fprintf(stderr, "#");
|
||||
if (size < 0) {
|
||||
perror("read vchan");
|
||||
libvchan_close(ctrl);
|
||||
exit(1);
|
||||
}
|
||||
if (size == 0)
|
||||
break;
|
||||
size = write_all(1, buf, size);
|
||||
if (size < 0) {
|
||||
perror("stdout write");
|
||||
exit(1);
|
||||
}
|
||||
if (size == 0) {
|
||||
perror("write size=0?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void writer(struct libvchan *ctrl)
|
||||
{
|
||||
int size;
|
||||
for (;;) {
|
||||
size = rand() % (BUFSIZE - 1) + 1;
|
||||
size = read(0, buf, size);
|
||||
if (size < 0) {
|
||||
perror("read stdin");
|
||||
libvchan_close(ctrl);
|
||||
exit(1);
|
||||
}
|
||||
if (size == 0)
|
||||
break;
|
||||
size = libvchan_write_all(ctrl, buf, size);
|
||||
fprintf(stderr, "#");
|
||||
if (size < 0) {
|
||||
perror("vchan write");
|
||||
exit(1);
|
||||
}
|
||||
if (size == 0) {
|
||||
perror("write size=0?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Simple libvchan application, both client and server.
|
||||
One side does writing, the other side does reading; both from
|
||||
standard input/output fds.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int seed = time(0);
|
||||
struct libvchan *ctrl = 0;
|
||||
int wr;
|
||||
if (argc < 4)
|
||||
usage();
|
||||
if (!strcmp(argv[2], "read"))
|
||||
wr = 0;
|
||||
else if (!strcmp(argv[2], "write"))
|
||||
wr = 1;
|
||||
else
|
||||
usage();
|
||||
if (!strcmp(argv[1], "server"))
|
||||
ctrl = libvchan_server_init(atoi(argv[3]));
|
||||
else if (!strcmp(argv[1], "client"))
|
||||
ctrl = libvchan_client_init(atoi(argv[3]), atoi(argv[4]));
|
||||
else
|
||||
usage();
|
||||
if (!ctrl) {
|
||||
perror("libvchan_*_init");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
fprintf(stderr, "seed=%d\n", seed);
|
||||
if (wr)
|
||||
writer(ctrl);
|
||||
else
|
||||
reader(ctrl);
|
||||
libvchan_close(ctrl);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user