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;
 | 
			
		||||
}
 | 
			
		||||
		Caricamento…
	
		Fai riferimento in un nuovo problema
	
	Block a user