/** Simple libvchan application, both client and server. One side does writing, the other side does reading. */ int main(int argc, char **argv) { struct libvchan *ctrl = 0; int wr; if (argc < 6) usage(argv); if (!strcmp(argv[2], "read")) wr = 0; else if (!strcmp(argv[2], "write")) wr = 1; else usage(argv); blocksize = atoi(argv[5]); total_size = atoll(argv[6]); buf = (char*) malloc(blocksize); if (buf == NULL) { perror("malloc"); exit(1); } printf("Running bandwidth test with domain %d on port %d, blocksize %d transfer_size %llu\n", atoi(argv[3]), atoi(argv[4]), blocksize, total_size); if (!strcmp(argv[1], "server")) { if (argc < 8) usage(argv); ctrl = libvchan_server_init(atoi(argv[3]), atoi(argv[4]), atoi(argv[7]), atoi(argv[8])); } else if (!strcmp(argv[1], "client")) ctrl = libvchan_client_init(atoi(argv[3]), atoi(argv[4])); else usage(argv); if (!ctrl) { perror("libvchan_*_init"); exit(1); } if (wr) writer(ctrl); else reader(ctrl); libvchan_close(ctrl); return 0; }
int main(int argc, char **argv) { int opt; char *domname = NULL; libvchan_t *data_vchan = NULL; int data_port; int data_domain; int msg_type; int s; int just_exec = 0; int connect_existing = 0; char *local_cmdline = NULL; char *remote_cmdline = NULL; char *request_id; char *src_domain_name = NULL; int src_domain_id = 0; /* if not -c given, the process is run in dom0 */ struct service_params svc_params; while ((opt = getopt(argc, argv, "d:l:ec:tT")) != -1) { switch (opt) { case 'd': domname = strdup(optarg); break; case 'l': local_cmdline = strdup(optarg); break; case 'e': just_exec = 1; break; case 'c': parse_connect(optarg, &request_id, &src_domain_name, &src_domain_id); connect_existing = 1; is_service = 1; break; case 't': replace_esc_stdout = 1; break; case 'T': replace_esc_stderr = 1; break; default: usage(argv[0]); } } if (optind >= argc || !domname) usage(argv[0]); remote_cmdline = argv[optind]; register_exec_func(&do_exec); if (just_exec + connect_existing + (local_cmdline != 0) > 1) { fprintf(stderr, "ERROR: only one of -e, -l, -c can be specified\n"); usage(argv[0]); } if (strcmp(domname, "dom0") == 0 && !connect_existing) { fprintf(stderr, "ERROR: when target domain is 'dom0', -c must be specified\n"); usage(argv[0]); } if (strcmp(domname, "dom0") == 0) { if (connect_existing) { msg_type = MSG_SERVICE_CONNECT; strncpy(svc_params.ident, request_id, sizeof(svc_params.ident)); } else if (just_exec) msg_type = MSG_JUST_EXEC; else msg_type = MSG_EXEC_CMDLINE; assert(src_domain_name); setenv("QREXEC_REMOTE_DOMAIN", src_domain_name, 1); s = connect_unix_socket(src_domain_name); negotiate_connection_params(s, 0, /* dom0 */ msg_type, connect_existing ? (void*)&svc_params : (void*)remote_cmdline, connect_existing ? sizeof(svc_params) : strlen(remote_cmdline) + 1, &data_domain, &data_port); prepare_local_fds(remote_cmdline); if (connect_existing) data_vchan = libvchan_client_init(data_domain, data_port); else { data_vchan = libvchan_server_init(data_domain, data_port, VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); while (data_vchan && libvchan_is_open(data_vchan) == VCHAN_WAITING) libvchan_wait(data_vchan); } if (!data_vchan || !libvchan_is_open(data_vchan)) { fprintf(stderr, "Failed to open data vchan connection\n"); do_exit(1); } if (handle_agent_handshake(data_vchan, connect_existing) < 0) do_exit(1); select_loop(data_vchan); } else { if (just_exec) msg_type = MSG_JUST_EXEC; else msg_type = MSG_EXEC_CMDLINE; s = connect_unix_socket(domname); negotiate_connection_params(s, src_domain_id, msg_type, remote_cmdline, strlen(remote_cmdline) + 1, &data_domain, &data_port); close(s); setenv("QREXEC_REMOTE_DOMAIN", domname, 1); prepare_local_fds(local_cmdline); if (connect_existing) { s = connect_unix_socket(src_domain_name); send_service_connect(s, request_id, data_domain, data_port); close(s); } else { data_vchan = libvchan_server_init(data_domain, data_port, VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); if (!data_vchan) { fprintf(stderr, "Failed to start data vchan server\n"); do_exit(1); } while (libvchan_is_open(data_vchan) == VCHAN_WAITING) libvchan_wait(data_vchan); if (!libvchan_is_open(data_vchan)) { fprintf(stderr, "Failed to open data vchan connection\n"); do_exit(1); } if (handle_agent_handshake(data_vchan, 0) < 0) do_exit(1); select_loop(data_vchan); } } return 0; }
/* do the preparatory tasks, needed before entering the main event loop */ void init(int xid) { char qrexec_error_log_name[256]; int logfd; int i; pid_t pid; int startup_timeout = MAX_STARTUP_TIME_DEFAULT; const char *startup_timeout_str = NULL; if (xid <= 0) { fprintf(stderr, "domain id=0?\n"); exit(1); } startup_timeout_str = getenv("QREXEC_STARTUP_TIMEOUT"); if (startup_timeout_str) { startup_timeout = atoi(startup_timeout_str); if (startup_timeout <= 0) // invalid or negative number startup_timeout = MAX_STARTUP_TIME_DEFAULT; } signal(SIGUSR1, sigusr1_handler); signal(SIGCHLD, sigchld_parent_handler); switch (pid=fork()) { case -1: perror("fork"); exit(1); case 0: break; default: if (getenv("QREXEC_STARTUP_NOWAIT")) exit(0); if (!opt_quiet) fprintf(stderr, "Waiting for VM's qrexec agent."); for (i=0;i<startup_timeout;i++) { sleep(1); if (!opt_quiet) fprintf(stderr, "."); if (i==startup_timeout-1) { break; } } fprintf(stderr, "Cannot connect to '%s' qrexec agent for %d seconds, giving up\n", remote_domain_name, startup_timeout); exit(3); } close(0); snprintf(qrexec_error_log_name, sizeof(qrexec_error_log_name), "/var/log/qubes/qrexec.%s.log", remote_domain_name); umask(0007); // make the log readable by the "qubes" group logfd = open(qrexec_error_log_name, O_WRONLY | O_CREAT | O_TRUNC, 0660); if (logfd < 0) { perror("open"); exit(1); } dup2(logfd, 1); dup2(logfd, 2); chdir("/var/run/qubes"); if (setsid() < 0) { perror("setsid()"); exit(1); } vchan = libvchan_client_init(xid, VCHAN_BASE_PORT); if (!vchan) { perror("cannot connect to qrexec agent"); exit(1); } if (handle_agent_hello(vchan, remote_domain_name) < 0) { exit(1); } if (setgid(getgid()) < 0) { perror("setgid()"); exit(1); } if (setuid(getuid()) < 0) { perror("setuid()"); exit(1); } /* initialize clients state arrays */ for (i = 0; i < MAX_CLIENTS; i++) { clients[i].state = CLIENT_INVALID; policy_pending[i].pid = 0; used_vchan_ports[i] = VCHAN_PORT_UNUSED; vchan_port_notify_client[i] = VCHAN_PORT_UNUSED; } /* When running as root, make the socket accessible; perms on /var/run/qubes still apply */ umask(0); qrexec_daemon_unix_socket_fd = create_qrexec_socket(xid, remote_domain_name); umask(0077); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, sigchld_handler); signal(SIGUSR1, SIG_DFL); kill(getppid(), SIGUSR1); // let the parent know we are ready }