qrexec* tools, initial version

This commit is contained in:
Rafal Wojtczuk 2011-03-04 16:32:58 +01:00
parent 80826329a7
commit b98dffc965
16 changed files with 1766 additions and 0 deletions

13
qrexec/Makefile Normal file
View File

@ -0,0 +1,13 @@
CC=gcc
CFLAGS+=-g -Wall
XENLIBS=-lvchan -lxenstore -lxenctrl
all: qrexec_daemon qrexec_agent qrexec_client
qrexec_daemon: qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o buffer.o write_stdin.o
$(CC) -g -o qrexec_daemon qrexec_daemon.o unix_server.o ioall.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
qrexec_agent: qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o
$(CC) -g -o qrexec_agent qrexec_agent.o ioall.o exec.o txrx-vchan.o write_stdin.o buffer.o $(XENLIBS)
qrexec_client: qrexec_client.o ioall.o exec.o
$(CC) -g -o qrexec_client qrexec_client.o ioall.o exec.o
clean:
rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client

93
qrexec/buffer.c Normal file
View 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
View 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
View 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];
}
}

49
qrexec/glue.h Normal file
View File

@ -0,0 +1,49 @@
/*
* 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 write_all(int fd, void *buf, int size);
int read_all(int fd, void *buf, int size);
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);
int do_accept(int s);
void set_nonblock(int fd);
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);

67
qrexec/ioall.c Normal file
View File

@ -0,0 +1,67 @@
/*
* 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>
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 <= 0) {
perror("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 == 0) {
fprintf(stderr, "EOF\n");
return 0;
}
if (ret < 0) {
perror("read");
return 0;
}
got_read += ret;
}
// fprintf(stderr, "read %d bytes\n", size);
return 1;
}
void set_nonblock(int fd)
{
int fl = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

58
qrexec/qrexec.h Normal file
View File

@ -0,0 +1,58 @@
/*
* 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
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_SERVER_TO_CLIENT_STDOUT,
MSG_SERVER_TO_CLIENT_STDERR,
MSG_SERVER_TO_CLIENT_EXIT_CODE
};
struct server_header {
unsigned int type;
unsigned int clid;
unsigned int len;
};
struct client_header {
unsigned int type;
unsigned int len;
};

452
qrexec/qrexec_agent.c Normal file
View 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 <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.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;
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];
void init()
{
peer_server_init(REXEC_PORT);
}
void do_exec(char *cmd)
{
char *sep = index(cmd, ':');
if (!sep) {
fprintf(stderr,
"cmdline is supposed to be in user:command form\n");
exit(1);
}
*sep = 0;
signal(SIGCHLD, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
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;
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;
kill(client_info[clid].pid, SIGKILL);
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) {
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;
}
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;
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);
}
}
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();
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();
}
}

236
qrexec/qrexec_client.c Normal file
View File

@ -0,0 +1,236 @@
/*
* 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 "qrexec.h"
#include "buffer.h"
#include "glue.h"
int connect_unix_socket(int domid)
{
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.%d", domid);
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 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");
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");
exit(1);
}
if (ret == 0) {
local_stdout_fd = -1;
shutdown(s, SHUT_WR);
}
if (!write_all(s, buf, ret)) {
perror("write daemon");
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");
exit(1);
}
if (hdr.len > MAX_DATA_CHUNK) {
fprintf(stderr, "client_header.len=%d\n", hdr.len);
exit(1);
}
if (!read_all(s, buf, hdr.len)) {
perror("read daemon");
exit(1);
}
switch (hdr.type) {
case MSG_SERVER_TO_CLIENT_STDOUT:
if (hdr.len == 0)
close(local_stdin_fd);
else
write_all(local_stdin_fd, buf, hdr.len);
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))
exit(WEXITSTATUS(status));
else
exit(255);
break;
default:
fprintf(stderr, "unknown msg %d\n", hdr.type);
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");
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");
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;
int domid = 0;
int s;
int just_exec = 0;
char *local_cmdline = NULL;
while ((opt = getopt(argc, argv, "d:l:e")) != -1) {
switch (opt) {
case 'd':
domid = atoi(optarg);
break;
case 'l':
local_cmdline = strdup(optarg);
break;
case 'e':
just_exec = 1;
break;
default:
usage(argv[0]);
}
}
if (optind >= argc || !domid)
usage(argv[0]);
s = connect_unix_socket(domid);
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;
}

319
qrexec/qrexec_daemon.c Normal file
View File

@ -0,0 +1,319 @@
/*
* 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 "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 init(int xid)
{
if (xid <= 0) {
fprintf(stderr, "domain id=0?\n");
exit(1);
}
server_fd = get_server_socket(xid);
peer_client_init(xid, REXEC_PORT);
setuid(getuid());
signal(SIGPIPE, SIG_IGN);
}
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;
}
void flush_client(int fd)
{
int i;
struct server_header s_hdr;
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);
}
}
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.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);
}
}

207
qrexec/txrx-vchan.c Normal file
View 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;
}

70
qrexec/unix_server.c Normal file
View File

@ -0,0 +1,70 @@
/*
* 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)
{
struct sockaddr_un sockname;
int s;
char socket_address[40];
snprintf(socket_address, sizeof(socket_address),
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid);
unlink(socket_address);
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;
}

85
qrexec/write_stdin.c Normal file
View File

@ -0,0 +1,85 @@
/*
* 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 "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;
ret = write(fd, data, len);
if (ret == len)
return WRITE_STDIN_OK;
if (ret == -1) {
if (errno == EAGAIN) {
struct server_header s_hdr;
buffer_append(buffer, data, len);
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;
} else
return WRITE_STDIN_ERROR;
} else {
fprintf(stderr,
"writes < PIPE_BUF were supposed to be atomic ?\n");
return WRITE_STDIN_ERROR;
}
}

View File

@ -60,6 +60,7 @@ fi
%build
make clean all
make -C ../common
make -C ../qrexec
%install
@ -72,6 +73,7 @@ mkdir -p $RPM_BUILD_ROOT/usr/bin
cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $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 ../qrexec/qrexec_agent $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}
@ -199,6 +201,7 @@ rm -rf $RPM_BUILD_ROOT
%{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
/etc/udev/rules.d/qubes.rules
/etc/sysconfig/iptables
/var/lib/qubes

View File

@ -49,6 +49,7 @@ python -m compileall qvm-core qmemman
python -O -m compileall qvm-core qmemman
make -C restore
make -C ../common
make -C ../qrexec
%install
@ -86,6 +87,8 @@ 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
@ -276,6 +279,8 @@ fi
/usr/lib/qubes/qubes_prepare_saved_domain.sh
/etc/xen/scripts/block.qubes
/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

View File

@ -50,6 +50,7 @@ fi
%build
make -C ../qrexec
%install
@ -61,6 +62,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/init.d
cp qubes_core $RPM_BUILD_ROOT/etc/init.d/
mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
cp ../qrexec/qrexec_agent $RPM_BUILD_ROOT/usr/lib/qubes
cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes
cp ../common/qubes_fix_nm_conf.sh $RPM_BUILD_ROOT/usr/lib/qubes
mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d
@ -175,6 +177,7 @@ rm -rf $RPM_BUILD_ROOT
/etc/sysconfig/iptables
/etc/init.d/qubes_core
/var/lib/qubes
/usr/lib/qubes/qrexec_agent
/usr/lib/qubes/qubes_setup_dnat_to_ns
/usr/lib/qubes/qubes_fix_nm_conf.sh
/etc/dhclient.d/qubes_setup_dnat_to_ns.sh