示例#1
0
void debugger_signal_handler(int n)
{
    unsigned tid;
    int s;

    /* avoid picking up GC interrupts */
    signal(SIGUSR1, SIG_IGN);

    tid = gettid();
    s = socket_local_client("android:debuggerd",
            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);

    if(s >= 0) {
        /* debugger knows our pid from the credentials on the
         * local socket but we need to tell it our tid.  It
         * is paranoid and will verify that we are giving a tid
         * that's actually in our process
         */
        int  ret;

        RETRY_ON_EINTR(ret, write(s, &tid, sizeof(unsigned)));
        if (ret == sizeof(unsigned)) {
            /* if the write failed, there is no point to read on
             * the file descriptor. */
            RETRY_ON_EINTR(ret, read(s, &tid, 1));
            notify_gdb_of_libraries();
        }
        close(s);
    }

    /* remove our net so we fault for real when we return */
    signal(n, SIG_IGN);
}
void
post_data_write_to_fp(GArray *post_data, guint idx, FILE *fp)
{
    char buf[128 * 1024];
    struct post_data_item_s *pdi = &g_array_index(post_data, struct post_data_item_s, idx);

    if (pdi->file_ref != 0) {
        int fd = -1;
        if (ppb_flash_file_file_ref_open_file(pdi->file_ref, PP_FILEOPENFLAG_READ, &fd) != PP_OK) {
            // some error, skipping this one
            goto err;
        }

        size_t to_write = post_data_get_item_length(pdi);
        while (to_write > 0) {
            ssize_t read_bytes = RETRY_ON_EINTR(read(fd, buf, MIN(to_write, sizeof(buf))));
            if (read_bytes == -1)
                goto err;
            fwrite(buf, 1, (size_t)read_bytes, fp);
            to_write -= read_bytes;
        }
err:
        if (fd >= 0)
            close(fd);
    } else {
        fwrite(pdi->data, 1, pdi->len, fp);
    }
}
示例#3
0
static int test_signal_handler(const char *argv0, const char *tempdir, int sig)
{
	int ret, pid, status;
	char err[512] = { 0 };
	char crash_log_path[PATH_MAX];
	snprintf(crash_log_path, sizeof(crash_log_path), "%s/crash.log.%d",
		 tempdir, rand());
	pid = fork();
	if (pid == -1) {
		ret = errno;
		return ret;
	}
	else if (pid == 0) {
		struct logc lc;
		memset(&lc, 0, sizeof(lc));
		lc.crash_log_path = crash_log_path;
		signal_init(argv0, err, sizeof(err), &lc);
		if (err[0]) {
			fprintf(stderr, "signal_init error: %s\n", err);
			_exit(1);
		}
		raise(sig);
		_exit(1);
	}
	RETRY_ON_EINTR(ret, waitpid(pid, &status, 0));

	EXPECT_ZERO(validate_crash_log(crash_log_path, sig));
	return 0;
}
示例#4
0
文件: socket.c 项目: cmccabe/redfish
int do_socket(int domain, int type, int proto, enum redfish_plat_flags_t pf)
{
	int res, ret, fd;
	fd = socket(domain, type, proto);
	if (fd < 0) {
		return -errno;
	}
	/* todo: mutex here */
	if (pf & WANT_O_CLOEXEC) {
		int flags = fcntl(fd, F_GETFD);
		flags |= FD_CLOEXEC;
		fcntl(fd, F_SETFD, flags);
	}
	if (pf & WANT_O_NONBLOCK) {
		int on = 1;
		ret = ioctl(fd, FIONBIO, (char*)&on);
		if (ret < 0) {
			RETRY_ON_EINTR(res, close(fd));
			return ret;
		}
	}
#if SO_REUSEADDR_HACK
	{
		int optval = 1;
		setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
			   &optval, sizeof(optval));
	}
#endif
	if (pf & WANT_TCP_NODELAY) {
		int optval = 1;
		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
			   &optval, sizeof(optval));
	}
	return fd;
}
示例#5
0
static void glitch_log_impl(const char *fmt, va_list ap)
{
	char *buf;
	va_list ap2;
	va_copy(ap2, ap);
	int ret, txt_sz = vsnprintf(NULL, 0, fmt, ap);
	buf = malloc(txt_sz + 1);
	if (!buf) {
		static const char OOM_MSG[] = "error writing to log: "
						"out of memory\n";
		glitch_log_to_syslog_and_stderr(OOM_MSG, sizeof(OOM_MSG) - 1);
		return;
	}
	vsnprintf(buf, txt_sz + 1, fmt, ap2);
	if (g_glitch_log_fd != -1) {
		ret = safe_write(g_glitch_log_fd, buf, txt_sz);
		if (ret != 0) {
			char err[512];
			snprintf(err, sizeof(err), "error writing to log "
				 "file '%s': error %d\n",
				 g_glitch_log_fname, ret);
			glitch_log_to_syslog_and_stderr(err, strlen(err));
			g_glitch_log_fd = -1;
			g_glitch_log_fname[0] = '\0';
			RETRY_ON_EINTR(ret, close(g_glitch_log_fd));
		}
	}
	glitch_log_to_syslog_and_stderr(buf, txt_sz);
	free(buf);
}
示例#6
0
void configure_glitch_log(const struct log_config *lc)
{
	int ret;
	int nfd = -1;

	pthread_mutex_lock(&g_glitch_log_lock);
	if (g_configured) {
		pthread_mutex_unlock(&g_glitch_log_lock);
		glitch_log("glitch log already configured.\n");
		return;
	}
	g_use_syslog = lc->use_syslog;
	if (lc->glitch_log) {
		RETRY_ON_EINTR(nfd, open(lc->glitch_log,
			O_WRONLY | O_CREAT | O_TRUNC, 0644));
		if (nfd == -1) {
			char err[512];
			ret = errno;
			snprintf(err, sizeof(err), "configure_glitch_log: "
				 "error opening '%s': error %d\n",
				 lc->glitch_log, ret);
			glitch_log_to_syslog_and_stderr(err, strlen(err));
		}
	}
	if (g_glitch_log_fd != -1) {
		char line[512], *last_slash;
		regurgitate_fd(line, sizeof(line), g_glitch_log_fd,
				nfd, g_use_syslog);
		RETRY_ON_EINTR(ret, close(g_glitch_log_fd));
		unlink(g_glitch_log_fname);
		last_slash = rindex(g_glitch_log_fname, '/');
		if (last_slash) {
			*last_slash = '\0';
			remove_tempdir(g_glitch_log_fname);
			unregister_tempdir_for_cleanup(g_glitch_log_fname);
		}
	}
	if (lc->glitch_log) {
		g_glitch_log_fd = nfd;
		snprintf(g_glitch_log_fname, PATH_MAX, lc->glitch_log);
	}
	else {
		g_glitch_log_fd = -1;
		g_glitch_log_fname[0] = '\0';
	}
	pthread_mutex_unlock(&g_glitch_log_lock);
}
int32_t
ppb_url_loader_read_response_body(PP_Resource loader, void *buffer, int32_t bytes_to_read,
                                  struct PP_CompletionCallback callback)
{
    struct url_loader_read_task_s *rt;
    int32_t read_bytes = PP_ERROR_FAILED;
    struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER);
    if (!ul) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (ul->fd == -1) {
        trace_error("%s, fd==-1\n", __func__);
        pp_resource_release(loader);
        return PP_ERROR_FAILED;
    }

    if (ul->read_tasks) {
        // schedule task instead of immediate reading if there is another task
        // in the queue already
        goto schedule_read_task;
    }

    read_bytes = -1;
    off_t ofs = lseek(ul->fd, ul->read_pos, SEEK_SET);
    if (ofs != (off_t)-1)
        read_bytes = RETRY_ON_EINTR(read(ul->fd, buffer, bytes_to_read));

    if (read_bytes < 0)
        read_bytes = PP_ERROR_FAILED;
    else
        ul->read_pos += read_bytes;

    if (read_bytes == 0 && !ul->finished_loading) {
        // no data ready, schedule read task
        goto schedule_read_task;
    }

    pp_resource_release(loader);
    if (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL)
        return read_bytes;

    ppb_message_loop_post_work_with_result(ppb_message_loop_get_current(), callback, 0, read_bytes,
                                           0, __func__);
    return PP_OK_COMPLETIONPENDING;

schedule_read_task:
    rt = g_slice_alloc(sizeof(*rt));
    rt->url_loader =    loader;
    rt->buffer =        buffer;
    rt->bytes_to_read = bytes_to_read;
    rt->ccb =           callback;
    rt->ccb_ml =        ppb_message_loop_get_current();

    ul->read_tasks = g_list_append(ul->read_tasks, rt);
    pp_resource_release(loader);
    return PP_OK_COMPLETIONPENDING;
}
示例#8
0
文件: signal.c 项目: cmccabe/redfish
void signal_shutdown(void)
{
	int res, i;
	for (i = 0; i < NUM_FATAL_SIGNALS; ++i) {
		signal(FATAL_SIGNALS[i], SIG_DFL);
	}
	signal(SIGPIPE, SIG_DFL);
	free(g_alt_stack.ss_sp);
	g_alt_stack.ss_sp = NULL;
	if (should_close_fd(g_crash_log_fd))
		RETRY_ON_EINTR(res, close(g_crash_log_fd));
	g_crash_log_fd = -1;
	if (should_close_fd(g_fast_log_fd))
		RETRY_ON_EINTR(res, close(g_fast_log_fd));
	g_fast_log_fd = -1;
	g_use_syslog = 0;
}
static
void
wakeup_audio_thread(void)
{
    g_atomic_int_set(&rebuild_fds, 1);
    RETRY_ON_EINTR(write(notification_pipe[1], "+", 1));
    pthread_barrier_wait(&stream_list_update_barrier);
}
static
void
drain_wakeup_pipe(int fd)
{
    char buf[8];
    while (RETRY_ON_EINTR(read(fd, buf, sizeof(buf))) > 0) {
        // cycle here doing nothing
    }
}
示例#11
0
void close_glitch_log(void)
{
	pthread_mutex_lock(&g_glitch_log_lock);
	if (g_glitch_log_fd != -1) {
		int res;
		RETRY_ON_EINTR(res, close(g_glitch_log_fd));
		g_glitch_log_fd = -1;
	}
	g_configured = 0;
	pthread_mutex_unlock(&g_glitch_log_lock);
}
示例#12
0
static int msgr_test_conn_shutdown(void)
{
	int res;
	struct msgr *baz1_msgr, *baz2_msgr;
	char err[512] = { 0 };
	size_t err_len = sizeof(err);
	struct listen_info linfo;

	EXPECT_ZERO(sem_init(&g_msgr_test_baz_sem, 0, 0));

	baz1_msgr = msgr_init_helper(10, 10, 360, "baz1_msgr");
	baz2_msgr = msgr_init_helper(10, 10, 360, "baz2_msgr");
	memset(&linfo, 0, sizeof(linfo));
	linfo.cb = baz_cb;
	linfo.priv = NULL;
	linfo.port = MSGR_UNIT_PORT;
	msgr_listen(baz2_msgr, &linfo, err, err_len);
	if (err[0])
		goto handle_error;
	msgr_start(baz1_msgr, err, err_len);
	if (err[0])
		goto handle_error;
	msgr_start(baz2_msgr, err, err_len);
	if (err[0])
		goto handle_error;
	EXPECT_ZERO(send_foo_tr(baz1_msgr, baz_cb, ECANCELED));
	msgr_shutdown(baz1_msgr);
	EXPECT_ZERO(send_foo_tr(baz1_msgr, baz_cb, ECANCELED));
	RETRY_ON_EINTR(res, sem_wait(&g_msgr_test_baz_sem));
	RETRY_ON_EINTR(res, sem_wait(&g_msgr_test_baz_sem));
	EXPECT_ZERO(sem_destroy(&g_msgr_test_baz_sem));

	msgr_shutdown(baz2_msgr);
	msgr_free(baz1_msgr);
	msgr_free(baz2_msgr);
	return 0;

handle_error:
	fprintf(stderr, "msgr_test_conn_shutdown: got error %s\n", err);
	return 1;
}
示例#13
0
文件: stest.c 项目: cmccabe/redfish
int stest_finish(void)
{
	int res;

	stest_set_status(100);
	RETRY_ON_EINTR(res, close(g_percent_fd));
	g_percent_fd = -1;
	fclose(g_err_fp);
	g_err_fp = NULL;
	process_ctx_shutdown();
	return (g_saw_err) ? EXIT_FAILURE : EXIT_SUCCESS;
}
示例#14
0
文件: ceph.c 项目: Zanop/collectd
static void cconn_close(struct cconn *io)
{
    io->state = CSTATE_UNCONNECTED;
    if(io->asok != -1)
    {
        int res;
        RETRY_ON_EINTR(res, close(io->asok));
    }
    io->asok = -1;
    io->amt = 0;
    io->json_len = 0;
    sfree(io->json);
    io->json = NULL;
}
示例#15
0
文件: socket.c 项目: cmccabe/redfish
int do_accept(int sock, struct sockaddr *addr, socklen_t len,
		enum redfish_plat_flags_t pf)
{
	int ret, fd, POSSIBLY_UNUSED(res);
	socklen_t olen;

	olen = len;
	fd = accept(sock, addr, &olen);
	if (fd < 0) {
		return -errno;
	}
	if (olen > len) {
		RETRY_ON_EINTR(res, close(fd));
		return -ENOBUFS;
	}
	/* todo: mutex here */
	if (pf & WANT_O_CLOEXEC) {
		int flags = fcntl(fd, F_GETFD);
		flags |= FD_CLOEXEC;
		fcntl(fd, F_SETFD, flags);
	}
	if (pf & WANT_O_NONBLOCK) {
		int on = 1;
		ret = ioctl(fd, FIONBIO, (char*)&on);
		if (ret < 0) {
			RETRY_ON_EINTR(res, close(fd));
			return ret;
		}
	}
	if (pf & WANT_TCP_NODELAY) {
		int optval = 1;
		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
			   &optval, sizeof(optval));
	}
	return fd;
}
示例#16
0
文件: ceph.c 项目: Zanop/collectd
static int cconn_connect(struct cconn *io)
{
    struct sockaddr_un address;
    int flags, fd, err;
    if(io->state != CSTATE_UNCONNECTED)
    {
        ERROR("ceph plugin: cconn_connect: io->state != CSTATE_UNCONNECTED");
        return -EDOM;
    }
    fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if(fd < 0)
    {
        int err = -errno;
        ERROR("ceph plugin: cconn_connect: socket(PF_UNIX, SOCK_STREAM, 0) "
            "failed: error %d", err);
        return err;
    }
    memset(&address, 0, sizeof(struct sockaddr_un));
    address.sun_family = AF_UNIX;
    snprintf(address.sun_path, sizeof(address.sun_path), "%s",
            io->d->asok_path);
    RETRY_ON_EINTR(err,
        connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)));
    if(err < 0)
    {
        ERROR("ceph plugin: cconn_connect: connect(%d) failed: error %d",
            fd, err);
        close(fd);
        return err;
    }

    flags = fcntl(fd, F_GETFL, 0);
    if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0)
    {
        err = -errno;
        ERROR("ceph plugin: cconn_connect: fcntl(%d, O_NONBLOCK) error %d",
            fd, err);
        close(fd);
        return err;
    }
    io->asok = fd;
    io->state = CSTATE_WRITE_REQUEST;
    io->amt = 0;
    io->json_len = 0;
    io->json = NULL;
    return 0;
}
示例#17
0
static int rsem_wait_for_callback(const char *name, int zsock)
{
	int res, ret, fd;
	struct sockaddr_in addr;
	socklen_t addr_len;

	addr_len = sizeof(addr);
	fd = accept(zsock, (struct sockaddr *)&addr, &addr_len);
	if (fd < 0) {
		int ret = -errno;
		glitch_log("rsem_wait_for_callback: accept error %d\n", ret);
		return -EIO;
	}
	ret = rsem_wait_for_callback_impl(name, fd);
	RETRY_ON_EINTR(res, close(fd));
	return ret;
}
示例#18
0
static void open_temp_glitch_log(void)
{
	int ret;
	char tempdir[PATH_MAX];
	ret = get_tempdir(tempdir, PATH_MAX, 0755);
	if (ret)
		return;
	snprintf(g_glitch_log_fname, PATH_MAX, "%s/glitch_log.tmp.txt",
		 tempdir);
        RETRY_ON_EINTR(g_glitch_log_fd, open(g_glitch_log_fname,
			O_CREAT | O_RDWR, 0644));
	if (g_glitch_log_fd == -1) {
		g_glitch_log_fname[0] = '\0';
		remove_tempdir(tempdir);
		return;
	}
	register_tempdir_for_cleanup(tempdir);
}
示例#19
0
static int msgr_test_simple_send(int num_sends)
{
	int i, res;
	struct msgr *foo_msgr, *bar_msgr;
	char err[512] = { 0 };
	size_t err_len = sizeof(err);
	struct listen_info linfo;

	EXPECT_ZERO(sem_init(&g_msgr_test_simple_send_sem, 0, 0));

	foo_msgr = msgr_init_helper(10, 10, 360, "foo_msgr");
	bar_msgr = msgr_init_helper(10, 10, 360, "bar_msgr");
	memset(&linfo, 0, sizeof(linfo));
	linfo.cb = bar_cb;
	linfo.priv = NULL;
	linfo.port = MSGR_UNIT_PORT;
	msgr_listen(bar_msgr, &linfo, err, err_len);
	if (err[0])
		goto handle_error;
	msgr_start(foo_msgr, err, err_len);
	if (err[0])
		goto handle_error;
	msgr_start(bar_msgr, err, err_len);
	if (err[0])
		goto handle_error;
	for (i = 0; i < num_sends; ++i) {
		EXPECT_ZERO(send_foo_tr(foo_msgr, foo_cb, i + 1));
	}
	for (i = 0; i < num_sends; ++i) {
		RETRY_ON_EINTR(res, sem_wait(&g_msgr_test_simple_send_sem));
	}
	EXPECT_ZERO(sem_destroy(&g_msgr_test_simple_send_sem));

	msgr_shutdown(foo_msgr);
	msgr_shutdown(bar_msgr);
	msgr_free(foo_msgr);
	msgr_free(bar_msgr);
	return 0;

handle_error:
	fprintf(stderr, "msgr_test_simple_send: got error %s\n", err);
	return 1;
}
示例#20
0
int32_t
ppb_url_loader_read_response_body(PP_Resource loader, void *buffer, int32_t bytes_to_read,
                                  struct PP_CompletionCallback callback)
{
    int32_t read_bytes = PP_ERROR_FAILED;
    struct pp_url_loader_s *ul = pp_resource_acquire(loader, PP_RESOURCE_URL_LOADER);
    if (!ul) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (ul->fd >= 0) {
        read_bytes = -1;
        off_t ofs = lseek(ul->fd, ul->read_pos, SEEK_SET);
        if (ofs != (off_t)-1)
            read_bytes = RETRY_ON_EINTR(read(ul->fd, buffer, bytes_to_read));

        if (read_bytes < 0)
            read_bytes = PP_ERROR_FAILED;
        else
            ul->read_pos += read_bytes;
    }

    if (read_bytes == 0 && !ul->finished_loading) {
        // no data ready, schedule read task
        struct url_loader_read_task_s *rt = g_slice_alloc(sizeof(*rt));
        rt->buffer = buffer;
        rt->bytes_to_read = bytes_to_read;
        rt->ccb = callback;

        ul->read_tasks = g_list_append(ul->read_tasks, rt);
        pp_resource_release(loader);
        return PP_OK_COMPLETIONPENDING;
    }

    pp_resource_release(loader);
    if (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL)
        return read_bytes;

    ppb_core_call_on_main_thread(0, callback, read_bytes);
    return PP_OK_COMPLETIONPENDING;
}
示例#21
0
int rsem_wait(struct rsem_client *rcli, const char *name)
{
	char err[512] = { 0 };
	size_t err_len = sizeof(err);
	int ret, res, port, zfd = -1, zsock = -1;
	struct rsem_request req;
	struct json_object *jo;
	uint32_t ty, resp;
	struct sockaddr_in addr;

	port = wait_for_next_free_port(rcli);
	zsock = do_bind_and_listen(port, err, err_len);
	if (err[0]) {
		glitch_log("rsem_wait: do_bind_and_listen failed with "
			   "error '%s'\n", err);
		ret = -EIO;
		zsock = -1;
		goto done;
	}
	zfd = do_socket(AF_INET, SOCK_STREAM, 0, WANT_O_CLOEXEC);
	if (zfd < 0) {
		glitch_log("rsem_wait: socket error: %d\n", zfd);
		ret = EIO;
		goto done;
	}
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(get_first_ipv4_addr(rcli->srv_host,
						err, err_len));
	if (err[0]) {
		/* couldn't resolve hostname */
		ret = EIO;
		goto done;
	}
	addr.sin_port = htons(rcli->srv_port);
	if (connect(zfd, &addr, sizeof(addr)) < 0) {
		ret = errno;
		glitch_log("rsem_wait: failed to connect to %s: error %d\n",
			   rcli->srv_host, ret);
		goto done;
	}
	ty = htonl(RSEM_CLIENT_REQ_SEM);
	if (safe_write(zfd, &ty, sizeof(uint32_t))) {
		glitch_log("rsem_wait: short write of message type\n");
		ret = -EIO;
		goto done;
	}
	memset(&req, 0, sizeof(req));
	req.name = (char*)name;
	req.port = port;
	jo = JORM_TOJSON_rsem_request(&req);
	if (!jo) {
		ret = -ENOMEM;
		glitch_log("rsem_wait: out of memory\n");
		goto done;
	}
	if (blocking_write_json_req("rsem_wait", zfd, jo)) {
		ret = -EIO;
		json_object_put(jo);
		goto done;
	}
	json_object_put(jo);
	if (safe_read(zfd, &resp, sizeof(uint32_t)) != sizeof(uint32_t)) {
		glitch_log("rsem_wait: short read of response\n");
		ret = -EIO;
		goto done;
	}
	resp = ntohl(resp);
	RETRY_ON_EINTR(res, close(zfd));
	zfd = -1;
	if (resp == RSEM_SERVER_GIVE_SEM) {
		ret = 0;
	}
	else if (resp == RSEM_SERVER_DELAY_SEM) {
		ret = rsem_wait_for_callback(name, zsock);
	}
	else {
		glitch_log("rsem_wait: unexpected server reply %d\n",
				resp);
		ret = -EIO;
	}

done:
	if (zsock >= 0)
		RETRY_ON_EINTR(res, close(zsock));
	if (zfd >= 0)
		RETRY_ON_EINTR(res, close(zfd));
	release_port(rcli, port);
	return ret;
}
示例#22
0
int main(int argc, char **argv)
{
	int origParentPipe[2];
	int userUid;

	// Create /var/run/vizstack. No failure if we are unable to create it
	mkdir("/var/run/vizstack", 0755); // user=> rwx, others => rx

	if(access("/var/run/vizstack", F_OK)!=0)
	{
		fprintf(stderr, "ERROR: Directory /var/vizstack does not exist, or could not be created. I cannot proceed without this.\n");
		exit(-1);
	}

	if(getenv("VS_X_DEBUG"))
	{
		g_debugPrints = true;
	}

	userUid = getuid(); // get user ID to ask for the server to run for this account

	struct passwd pwd, *ppwd;
	char pwd_buffer[2048];

	// Find the invoking user ID
	if(getpwuid_r(getuid(), &pwd, pwd_buffer, sizeof(pwd_buffer), &ppwd)!=0)
	{
		perror("ERROR : Failed to find username of invoking user\n");
		exit(-1);
	}

	// Create a pipe to detect death of the parent process
	if (pipe(origParentPipe)<0)
	{
		perror("FATAL : Could not create pipe. System may be running out of resources");
		exit(-1);
	}

	// Fork here to control SUID child
	// Fork is needed else the caller of the GDMlauncher cannot control the
	// SUID child!
	int suidChildPid = fork();
	if(suidChildPid != 0)
	{
		parentWaitTillSUIDExits(suidChildPid, origParentPipe);
	}

	// Close the write end since we don't need to communicate
	// anything to the parent process
	close(origParentPipe[1]);

	if(pipe(g_signotify_pipe)!=0)
	{
		perror("ERROR: Unable to create resources for running this program.");
		exit(-1);
	}

	// Now shift to become the root user, since GDM needs to be run only as root
	// This is done by setting the real uid to 0.
	// This approach works as this binary is expected to be SUID root.
	int status;
	status = setreuid(0, 0);
	if (status != 0)
	{
        	perror("ERROR: Unable to set real user id to root");
	        exit(-1);
	}
	status = setregid(0, 0);
	if (status != 0)
	{
        	perror("ERROR: Unable to set real group id to root");
	        exit(-1);
	}

	// Check that the template gdm.conf can be found.
	FILE *fp = fopen(TEMPLATE_GDM_CONF, "r");
	if(!fp)
	{
		perror("ERROR : Unable to find the template gdm.conf file " TEMPLATE_GDM_CONF );
		char hostname[4096];
		strcpy(hostname,"");
		gethostname(hostname, sizeof(hostname));
		fprintf(stderr, "\n\nPlease contact your system administrator and ensure that the node '%s' has been setup sppropriately for RGS.\n",hostname);
		fprintf(stderr, "The instructions needed to do the setup are at the top of the file /etc/vizstack/templates/gdm.conf.template\n");
		exit(-1);
	}
	fclose(fp);

	// Patch the config file for the real user
	char cmd[256]; 
	char uidAsString[256];
	sprintf(uidAsString, "%d", userUid);
	sprintf(cmd, "sed %s -es/@@USER@@/%s/g -es/@@UID@@/%s/g > %s", TEMPLATE_GDM_CONF, pwd.pw_name, uidAsString, RUNTIME_GDM_CONF);
	if(system(cmd)!=0)
	{
		fprintf(stderr, "ERROR : Unable to generate a config file for the GDM session\n");
		exit(-1);
	}

	// Signal handlers for TERM and INT
	// This is done _after_ the system() call, else we'll have other SIGCHLD signals to
	// handle as well
	struct sigaction siginfo;
	siginfo.sa_handler = sigint_handler;
	siginfo.sa_flags = SA_RESTART; 
	sigemptyset (&siginfo.sa_mask);
	sigaction(SIGINT, &siginfo, NULL);

	siginfo.sa_handler = sigterm_handler;
	siginfo.sa_flags = SA_RESTART; 
	sigemptyset (&siginfo.sa_mask);
	sigaction(SIGTERM, &siginfo, NULL);

	siginfo.sa_handler = sigchild_handler;
	siginfo.sa_flags = SA_RESTART; 
	sigemptyset (&siginfo.sa_mask);
	sigaction(SIGCHLD, &siginfo, NULL);

	int childpid = fork();

	if(childpid<0)
	{
		fprintf(stderr,"CRITICAL ERROR : Couldn't fork!\n");
		exit(-1);
	}

	if(childpid==0)
	{
		// close the read end of the pipe. since we exec GDM next
		close(origParentPipe[0]);

		// child, we will exec GDM here in foreground mode
		char **childArgs=new char *[4];

		// Find the right GDM binary
		if(access(GDM_PATH_1, X_OK)==0)
		{
			childArgs[0]=GDM_PATH_1;
		}
		else
		if(access(GDM_PATH_2, X_OK)==0)
		{
			childArgs[0]=GDM_PATH_2;
		}
		else
		{
			fprintf(stderr, "ERROR : Cannot find gdm. Cannot continue.\n");
			exit(-1);
		}

		childArgs[1]="-nodaemon";
		char configOption[4096];
		sprintf(configOption,"--config=%s", RUNTIME_GDM_CONF);
		childArgs[2]=configOption;
		childArgs[3]=NULL;
		execv(childArgs[0], childArgs);

		// If exec failed, then we have an error
		perror("ERROR : failed to start GDM");
		exit(-1);
	}

	// the parent comes here
	// wait for GDM to exit
	// while handling signals at the same time
	fd_set rfds;
	bool loopDone=false;
	int retCode=0;

	while(!loopDone)
	{
		FD_ZERO (&rfds);
		FD_SET (g_signotify_pipe[0], &rfds);
		int maxFD = g_signotify_pipe[0];

		if(origParentPipe[0]!=-1)
			FD_SET (origParentPipe[0], &rfds);
		if (origParentPipe[0]>maxFD)
			maxFD = origParentPipe[0];

		if(g_debugPrints)
			printf("INFO : Waiting for child GDM\n");

		// NOTE: Infinite timeout select below
		int ret;
  		RETRY_ON_EINTR(ret,select (maxFD + 1, &rfds, NULL, NULL, NULL));

		// Handle Errors in select
		if (ret==-1)
		{
			// FIXME: humm - what can we do here ?
		}

		if ((origParentPipe[0]!=-1) && FD_ISSET(origParentPipe[0], &rfds))
		{
			// Do a read of 1 byte
			char buf=0;
			RETRY_ON_EINTR(ret, read(origParentPipe[0], &buf, 1));

			// If the SSM closes the socket, then we act as if we had got
			// SIGTERM
			if(ret==0)
			{
				if(g_debugPrints)
					printf("INFO : Parent closed connection. Killing GDM using SIGTERM\n");
				kill(childpid, SIGTERM);
				close(origParentPipe[0]);
				origParentPipe[0]=-1;
			}
			else
			{
				fprintf(stderr, "FATAL: Bad case - parent isn't supposed to write to us!\n");
				exit(-1);
			}
		}

		// determine what signal we received by
		// reading the pipe
		char buf=0;
		RETRY_ON_EINTR(ret, read(g_signotify_pipe[0], &buf, 1));

		switch(buf)
		{
			case CODE_SIGCHLD:
				// GDM exited
				{
					if(g_debugPrints)
						printf("INFO : GDM exited\n");
					loopDone = true;
					retCode = -1;
					int exitstatus;
					if(waitpid(childpid, &exitstatus, WNOHANG)==childpid)
					{
						if(WIFEXITED(exitstatus))
						{
							if(g_debugPrints)
								printf("INFO : Child X server exited\n");
							retCode = exitstatus;
						}
						else
							if(WIFSIGNALED(exitstatus))
							{

								// XXX : This rarely seems to show up in practise, due to signal handling
								// by the X server
								if(g_debugPrints)
									printf("INFO : Child X server killed by signal %d\n", WTERMSIG(exitstatus));

								retCode = 128+WTERMSIG(exitstatus);
							}
					}
				}
				break;
			case CODE_SIGTERM: 
				if(g_debugPrints)
					printf("INFO : Propagating SIGTERM to child\n");
				kill(childpid, SIGTERM);
				break;

			case CODE_SIGINT:
				if(g_debugPrints)
					printf("INFO : Propagating SIGINT to child\n");
				kill(childpid, SIGINT);
				break;
		}
	}

	unlink(RUNTIME_GDM_CONF);
	exit(retCode);
}
示例#23
0
//
// Function called by main program before it forks off to
// create the SUID root X server
//
void parentWaitTillSUIDExits(int suidChildPid, int origParentPipe[2])
{

	struct sigaction sighandler;

	// close the read end of the pipe, since we don't want any information
	// from the child process.
	close(origParentPipe[0]);

	pipe(g_parent_sig_pipe);

	sighandler.sa_handler = parent_sighdlr;
	sighandler.sa_flags = SA_RESTART;
	sigemptyset(&sighandler.sa_mask);

	// We handle these signals
	sigaction(SIGINT, &sighandler, NULL);
	sigaction(SIGTERM, &sighandler, NULL);
	sigaction(SIGCHLD, &sighandler, NULL);

	fd_set rfds;
	bool loopDone = false;
	int retCode = 0;

	while(!loopDone)
	{
		FD_ZERO (&rfds);
		FD_SET(g_parent_sig_pipe[0], &rfds);

		// NOTE: Infinite timeout select below
		int ret;
  		RETRY_ON_EINTR(ret,select (g_parent_sig_pipe[0] + 1, &rfds, NULL, NULL, NULL));

		// Handle Errors in select
		if (ret==-1)
		{
			// FIXME: humm - what can we do here ?
			continue;
		}

		// determine what signal we received by
		// reading the pipe
		char buf=0;
		RETRY_ON_EINTR(ret, read(g_parent_sig_pipe[0], &buf, 1));

		switch(buf)
		{
			case CODE_SIGCHLD:
			// handling SIGCHLD is how we get out of the loop and exit from the main program
			{
				loopDone = true;
				retCode = -1;
				int exitstatus;
				if(waitpid(suidChildPid, &exitstatus, WNOHANG)==suidChildPid)
				{
					if(WIFEXITED(exitstatus))
					{
						retCode = WEXITSTATUS(exitstatus);
					}
					else
					if(WIFSIGNALED(exitstatus))
					{
						// XXX : This rarely seems to show up in practise, due to signal handling
						// by the X server
						retCode = 128+WTERMSIG(exitstatus);
					}
				}
				break;
			}
			case CODE_SIGTERM:
			// propagate TERM to child X server. When that exits, we get SIGCHLD and then we exit
				kill(suidChildPid, SIGTERM);
				break;
			case CODE_SIGINT:
			// propagate INT to child X server. When that exits, we get SIGCHLD and then we exit
				kill(suidChildPid, SIGINT);
				break;
		}

	}

	// in fact, I think the below line is redundant code
	// why ? if normal control comes here, then the child has
	// already died. 
	// 
	// The real reason we use this pipe is : if this process is
	// forcefully terminated - e.g. by kill -9, then the child
	// will be able to detect that quickly, and kill the real X server
	// termination of us 
	close(origParentPipe[1]);

	exit(retCode);
}
示例#24
0
static int rsem_post_impl(struct rsem_client *rcli, const char *name)
{
	char err[512] = { 0 };
	size_t err_len = sizeof(err);
	int res, ret, zfd = -1;
	struct json_object *jo = NULL;
	struct sockaddr_in addr;
	uint32_t ty, resp;
	struct rsem_release rel;

	rel.name = (char*)name;
	jo = JORM_TOJSON_rsem_release(&rel);
	if (!jo) {
		ret = -ENOMEM;
		glitch_log("rsem_post_impl: out of memory\n");
		goto done;
	}
	zfd = do_socket(AF_INET, SOCK_STREAM, 0, WANT_O_CLOEXEC);
	if (zfd < 0) {
		ret = zfd;
		glitch_log("rsem_post_impl: socket error: %d\n", ret);
		goto done;
	}
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(get_first_ipv4_addr(rcli->srv_host,
				err, err_len));
	if (err[0]) {
		/* couldn't resolve hostname */
		ret = -EIO;
		goto done;
	}
	addr.sin_port = htons(rcli->srv_port);
	if (connect(zfd, &addr, sizeof(addr)) < 0) {
		ret = errno;
		glitch_log("rsem_post_impl: failed to connect to %s: "
			   "error %d\n", rcli->srv_host, ret);
		ret = -EIO;
		goto done;
	}
	ty = htonl(RSEM_CLIENT_REL_SEM);
	if (safe_write(zfd, &ty, sizeof(uint32_t))) {
		glitch_log("rsem_post_impl: short write of message type %d\n",
			   RSEM_CLIENT_REL_SEM);
		ret = -EIO;
		goto done;
	}
	if (blocking_write_json_req("rsem_post_impl", zfd, jo)) {
		ret = -EIO;
		goto done;
	}
	if (safe_read(zfd, &resp, sizeof(uint32_t)) != sizeof(uint32_t)) {
		glitch_log("rsem_post_impl: short read of response\n");
		ret = -EIO;
		goto done;
	}
	resp = ntohl(resp);
	if (resp == RSEM_SERVER_ACK) {
		ret = 0;
	}
	else {
		glitch_log("rsem_post_impl: got unexpected server "
			   "response %d\n", resp);
		ret = -EIO;
	}

done:
	if (jo)
		json_object_put(jo);
	if (zfd >= 0)
		RETRY_ON_EINTR(res, close(zfd));
	return ret;
}
示例#25
0
文件: ceph.c 项目: Zanop/collectd
/** Handle a network event for a connection */
static int cconn_handle_event(struct cconn *io)
{
    int ret;
    switch (io->state)
    {
        case CSTATE_UNCONNECTED:
            ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal "
                "state on line %d", io->d->name, __LINE__);

            return -EDOM;
        case CSTATE_WRITE_REQUEST:
        {
            char cmd[32];
            snprintf(cmd, sizeof(cmd), "%s%d%s", "{ \"prefix\": \"",
                    io->request_type, "\" }\n");
            size_t cmd_len = strlen(cmd);
            RETRY_ON_EINTR(ret,
                  write(io->asok, ((char*)&cmd) + io->amt, cmd_len - io->amt));
            DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,amt=%d,ret=%d)",
                    io->d->name, io->state, io->amt, ret);
            if(ret < 0)
            {
                return ret;
            }
            io->amt += ret;
            if(io->amt >= cmd_len)
            {
                io->amt = 0;
                switch (io->request_type)
                {
                    case ASOK_REQ_VERSION:
                        io->state = CSTATE_READ_VERSION;
                        break;
                    default:
                        io->state = CSTATE_READ_AMT;
                        break;
                }
            }
            return 0;
        }
        case CSTATE_READ_VERSION:
        {
            RETRY_ON_EINTR(ret,
                    read(io->asok, ((char*)(&io->d->version)) + io->amt,
                            sizeof(io->d->version) - io->amt));
            DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
                    io->d->name, io->state, ret);
            if(ret < 0)
            {
                return ret;
            }
            io->amt += ret;
            if(io->amt >= sizeof(io->d->version))
            {
                io->d->version = ntohl(io->d->version);
                if(io->d->version != 1)
                {
                    ERROR("ceph plugin: cconn_handle_event(name=%s) not "
                        "expecting version %d!", io->d->name, io->d->version);
                    return -ENOTSUP;
                }
                DEBUG("ceph plugin: cconn_handle_event(name=%s): identified as "
                        "version %d", io->d->name, io->d->version);
                io->amt = 0;
                cconn_close(io);
                io->request_type = ASOK_REQ_SCHEMA;
            }
            return 0;
        }
        case CSTATE_READ_AMT:
        {
            RETRY_ON_EINTR(ret,
                    read(io->asok, ((char*)(&io->json_len)) + io->amt,
                            sizeof(io->json_len) - io->amt));
            DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
                    io->d->name, io->state, ret);
            if(ret < 0)
            {
                return ret;
            }
            io->amt += ret;
            if(io->amt >= sizeof(io->json_len))
            {
                io->json_len = ntohl(io->json_len);
                io->amt = 0;
                io->state = CSTATE_READ_JSON;
                io->json = calloc(1, io->json_len + 1);
                if(!io->json)
                {
                    ERROR("ceph plugin: error callocing io->json");
                    return -ENOMEM;
                }
            }
            return 0;
        }
        case CSTATE_READ_JSON:
        {
            RETRY_ON_EINTR(ret,
                   read(io->asok, io->json + io->amt, io->json_len - io->amt));
            DEBUG("ceph plugin: cconn_handle_event(name=%s,state=%d,ret=%d)",
                    io->d->name, io->state, ret);
            if(ret < 0)
            {
                return ret;
            }
            io->amt += ret;
            if(io->amt >= io->json_len)
            {
                ret = cconn_process_json(io);
                if(ret)
                {
                    return ret;
                }
                cconn_close(io);
                io->request_type = ASOK_REQ_NONE;
            }
            return 0;
        }
        default:
            ERROR("ceph plugin: cconn_handle_event(name=%s) got to illegal "
                "state on line %d", io->d->name, __LINE__);
            return -EDOM;
    }
}
示例#26
0
文件: read.c 项目: cmccabe/redfish
int fishtool_read(struct fishtool_params *params)
{
	const char *path, *local;
	int fd = -1, res, ret;
	struct redfish_client *cli = NULL;
	struct redfish_file *ofe = NULL;
	char err[512] = { 0 };
	size_t err_len = sizeof(err);

	path = params->non_option_args[0];
	if (!path) {
		fprintf(stderr, "fishtool_read: you must give a path name "
			"to create. -h for help.\n");
		ret = -EINVAL;
		goto done;
	}
	local = params->lowercase_args[ALPHA_IDX('o')];
	cli = redfish_connect(params->cpath, params->user_name,
		redfish_log_to_stderr, NULL, err, err_len);
	if (err[0]) {
		fprintf(stderr, "redfish_connect: failed to connect: "
				"%s\n", err);
		ret = -EIO;
		goto done;
	}
	ret = redfish_open(cli, path, &ofe);
	if (ret) {
		fprintf(stderr, "redfish_open failed with error %d\n", ret);
		goto done;
	}
	if (local) {
		fd = open(local, O_TRUNC | O_CREAT | O_WRONLY);
		if (fd < 0) {
			ret = -errno;
			fprintf(stderr, "fishtool_read: error opening "
				"'%s' for write: %d\n", local, ret);
			goto done;
		}
	}
	else {
		local = "stdout";
		fd = STDOUT_FILENO;
	}
	while (1) {
		char buf[8192];

		memset(buf, 0, sizeof(buf));
		ret = redfish_read(ofe, buf, sizeof(buf));
		if (ret < 0) {
			fprintf(stderr, "redfish_read: error reading "
				"%Zd bytes from %s: %d\n", sizeof(buf), path, ret);
			goto done;
		}
		if (ret == 0) {
			break;
		}
		ret = safe_write(fd, buf, ret);
		if (ret) {
			fprintf(stderr, "fishtool_read: error writing "
				"to %s: %d\n", local, ret);
			goto done;
		}
	}
	ret = redfish_close(ofe);
	ofe = NULL;
	if (ret) {
		/* This shouldn't happen; getting errors from close only happens
		 * with a file that's open for write.  But let's check anyway.
		 */
		fprintf(stderr, "redfish_close failed with error %d\n", ret);
		goto done;
	}
	ret = 0;
done:
	if ((fd != STDOUT_FILENO) && (fd > 0)) {
		RETRY_ON_EINTR(res, close(fd));
	}
	if (ofe)
		redfish_free_file(ofe);
	if (cli)
		redfish_disconnect_and_release(cli);
	return ret;
}
示例#27
0
文件: ceph.c 项目: Zanop/collectd
/** This handles the actual network I/O to talk to the Ceph daemons.
 */
static int cconn_main_loop(uint32_t request_type)
{
    int i, ret, some_unreachable = 0;
    struct timeval end_tv;
    struct cconn io_array[g_num_daemons];

    DEBUG("ceph plugin: entering cconn_main_loop(request_type = %d)", request_type);

    /* create cconn array */
    memset(io_array, 0, sizeof(io_array));
    for(i = 0; i < g_num_daemons; ++i)
    {
        io_array[i].d = g_daemons[i];
        io_array[i].request_type = request_type;
        io_array[i].state = CSTATE_UNCONNECTED;
    }

    /** Calculate the time at which we should give up */
    gettimeofday(&end_tv, NULL);
    end_tv.tv_sec += CEPH_TIMEOUT_INTERVAL;

    while (1)
    {
        int nfds, diff;
        struct timeval tv;
        struct cconn *polled_io_array[g_num_daemons];
        struct pollfd fds[g_num_daemons];
        memset(fds, 0, sizeof(fds));
        nfds = 0;
        for(i = 0; i < g_num_daemons; ++i)
        {
            struct cconn *io = io_array + i;
            ret = cconn_prepare(io, fds + nfds);
            if(ret < 0)
            {
                WARNING("ceph plugin: cconn_prepare(name=%s,i=%d,st=%d)=%d",
                        io->d->name, i, io->state, ret);
                cconn_close(io);
                io->request_type = ASOK_REQ_NONE;
                some_unreachable = 1;
            }
            else if(ret == 1)
            {
                polled_io_array[nfds++] = io_array + i;
            }
        }
        if(nfds == 0)
        {
            /* finished */
            ret = 0;
            goto done;
        }
        gettimeofday(&tv, NULL);
        diff = milli_diff(&end_tv, &tv);
        if(diff <= 0)
        {
            /* Timed out */
            ret = -ETIMEDOUT;
            WARNING("ceph plugin: cconn_main_loop: timed out.");
            goto done;
        }
        RETRY_ON_EINTR(ret, poll(fds, nfds, diff));
        if(ret < 0)
        {
            ERROR("ceph plugin: poll(2) error: %d", ret);
            goto done;
        }
        for(i = 0; i < nfds; ++i)
        {
            struct cconn *io = polled_io_array[i];
            int revents = fds[i].revents;
            if(revents == 0)
            {
                /* do nothing */
            }
            else if(cconn_validate_revents(io, revents))
            {
                WARNING("ceph plugin: cconn(name=%s,i=%d,st=%d): "
                "revents validation error: "
                "revents=0x%08x", io->d->name, i, io->state, revents);
                cconn_close(io);
                io->request_type = ASOK_REQ_NONE;
                some_unreachable = 1;
            }
            else
            {
                int ret = cconn_handle_event(io);
                if(ret)
                {
                    WARNING("ceph plugin: cconn_handle_event(name=%s,"
                    "i=%d,st=%d): error %d", io->d->name, i, io->state, ret);
                    cconn_close(io);
                    io->request_type = ASOK_REQ_NONE;
                    some_unreachable = 1;
                }
            }
        }
    }
    done: for(i = 0; i < g_num_daemons; ++i)
    {
        cconn_close(io_array + i);
    }
    if(some_unreachable)
    {
        DEBUG("ceph plugin: cconn_main_loop: some Ceph daemons were unreachable.");
    }
    else
    {
        DEBUG("ceph plugin: cconn_main_loop: reached all Ceph daemons :)");
    }
    return ret;
}
int32_t
ppb_video_capture_open(PP_Resource video_capture, PP_Resource device_ref,
                       const struct PP_VideoCaptureDeviceInfo_Dev *requested_info,
                       uint32_t buffer_count, struct PP_CompletionCallback callback)
{
    int32_t result;
    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    const char *capture_device = default_capture_device;
    struct PP_Var longname = ppb_device_ref_get_longname(device_ref);

    if (longname.type == PP_VARTYPE_STRING)
        capture_device = ppb_var_var_to_utf8(longname, NULL);

    vc->fd = v4l2_open(capture_device, O_RDWR);

    ppb_var_release(longname);

    if (vc->fd < 0) {
        result = PP_ERROR_NOACCESS;
        goto point_1;
    }

    struct v4l2_capability caps;
    if (v4l2_ioctl(vc->fd, VIDIOC_QUERYCAP, &caps) != 0) {
        result = PP_ERROR_FAILED;
        goto point_2;
    }

#ifdef V4L2_CAP_DEVICE_CAPS
    const uint32_t device_caps = (caps.capabilities & V4L2_CAP_DEVICE_CAPS) ? caps.device_caps
                                                                            : caps.capabilities;
#else
    const uint32_t device_caps = caps.capabilities;
#endif // V4L2_CAP_DEVICE_CAPS

    if (!(device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
        trace_error("%s, device can't capture\n", __func__);
        result = PP_ERROR_FAILED;
        goto point_2;
    }

    if (!(device_caps & V4L2_CAP_READWRITE)) {
        trace_error("%s, device doesn't support read/write interface\n", __func__);
        result = PP_ERROR_FAILED;
        goto point_2;
    }

    if (requested_info) {
        vc->width =  requested_info->width;
        vc->height = requested_info->height;
        vc->fps =    requested_info->frames_per_second;
    } else {
        vc->width =  640;
        vc->height = 480;
        vc->fps =    15;
    }

    struct v4l2_format fmt = {
        .type =                 V4L2_BUF_TYPE_VIDEO_CAPTURE,
        .fmt.pix.width =        vc->width,
        .fmt.pix.height =       vc->height,
        .fmt.pix.pixelformat =  V4L2_PIX_FMT_YUV420,    // PPAPI hardcodes format to YUV420
        .fmt.pix.field =        V4L2_FIELD_INTERLACED,
    };

    if (v4l2_ioctl(vc->fd, VIDIOC_S_FMT, &fmt) != 0) {
        trace_error("%s, failed to set resolution\n", __func__);
        result = PP_ERROR_FAILED;
        goto point_2;
    }

    vc->width =  fmt.fmt.pix.width;
    vc->height = fmt.fmt.pix.height;

    vc->buffer_size = fmt.fmt.pix.sizeimage;    // buffer size in bytes
    vc->buffer_count = MAX(buffer_count, 5);    // limit lowest number of buffers, just in case

    vc->buffers = calloc(sizeof(*vc->buffers), vc->buffer_count);
    if (!vc->buffers) {
        trace_error("%s, memory allocation failure (1)\n", __func__);
        result = PP_ERROR_FAILED;
        goto point_2;
    }

    vc->buffer_is_free = malloc(sizeof(*vc->buffer_is_free) * vc->buffer_count);
    if (!vc->buffer_is_free) {
        trace_error("%s, memory allocation failure (2)\n", __func__);
        result = PP_ERROR_FAILED;
        goto point_3;
    }

    for (unsigned int k = 0; k < vc->buffer_count; k ++) {
        vc->buffer_is_free[k] = 1;
        vc->buffers[k] = ppb_buffer_create(vc->instance->id, vc->buffer_size);
        if (vc->buffers[k] == 0)
            goto point_4;
    }

    struct PP_VideoCaptureDeviceInfo_Dev info = {
        .width =             vc->width,
        .height =            vc->height,
        .frames_per_second = vc->fps,
    };

    vc->ppp_video_capture_dev->OnDeviceInfo(vc->instance->id, video_capture, &info,
                                            vc->buffer_count, vc->buffers);

    result = PP_OK;
    goto point_1;

point_4:
    for (unsigned int k = 0; k < vc->buffer_count; k ++)
        ppb_core_release_resource(vc->buffers[k]);
    free_and_nullify(vc->buffer_is_free);
point_3:
    free_and_nullify(vc->buffers);
point_2:
    v4l2_close(vc->fd);
    vc->fd = -1;
point_1:
    pp_resource_release(video_capture);
    ppb_core_call_on_main_thread2(0, callback, result, __func__);
    return PP_OK_COMPLETIONPENDING;
}

struct on_buffer_ready_param_s {
    PP_Instance                            instance;
    PP_Resource                            video_capture;
    uint32_t                               buf_idx;
    const struct PPP_VideoCapture_Dev_0_1 *ppp_video_capture_dev;
};

static
void
on_buffer_ready_comt(void *user_data, int32_t result)
{
    struct on_buffer_ready_param_s *p = user_data;
    struct pp_instance_s *pp_i = tables_get_pp_instance(p->instance);
    if (!pp_i)
        return;

    p->ppp_video_capture_dev->OnBufferReady(p->instance, p->video_capture, p->buf_idx);
    g_slice_free1(sizeof(*p), p);
}

static
void *
video_capture_thread(void *param)
{
    struct pp_video_capture_s *vc = param;

    PP_Resource  video_capture = vc->self_id;
    PP_Instance  instance = vc->instance->id;
    const int    fd = vc->fd;
    const size_t buffer_size = vc->buffer_size;

    vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc)
        goto gone;

    while (!vc->terminate_thread) {
        // find free buffer
        uint32_t buf_idx = (uint32_t)-1;
        for (uint32_t k = 0; k < vc->buffer_count; k ++) {
            if (vc->buffer_is_free[k]) {
                buf_idx = k;
                vc->buffer_is_free[k] = 0;
                break;
            }
        }

        if (buf_idx == (uint32_t)-1) {
            // all buffers are busy, wait for some to free, with resource unlocked
            pp_resource_release(video_capture);
            usleep(10);
            vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
            if (!vc)
                goto gone;
            continue;
        }

        PP_Resource buffer = vc->buffers[buf_idx];
        pp_resource_release(video_capture);

        // wait on v4l2_read() with resource unlocked
        void *ptr = ppb_buffer_map(buffer);
        RETRY_ON_EINTR(v4l2_read(fd, ptr, buffer_size));
        ppb_buffer_unmap(buffer);

        vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
        if (!vc)
            goto gone;

        struct on_buffer_ready_param_s *p = g_slice_alloc(sizeof(*p));
        p->instance =               instance;
        p->video_capture =          video_capture;
        p->buf_idx =                buf_idx;
        p->ppp_video_capture_dev =  vc->ppp_video_capture_dev;
        ppb_core_call_on_main_thread2(0, PP_MakeCCB(on_buffer_ready_comt, p), PP_OK, __func__);
    }

    pp_resource_release(video_capture);
    return NULL;

gone:
    trace_error("%s, resource gone\n", __func__);
    return NULL;
}

int32_t
ppb_video_capture_start_capture(PP_Resource video_capture)
{
    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (vc->thread_started)
        goto done;

    if (vc->fd < 0) {
        trace_error("%s, device is closed\n", __func__);
        pp_resource_release(video_capture);
        return PP_ERROR_FAILED;
    }

    vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture,
                                        PP_VIDEO_CAPTURE_STATUS_STARTING);

    pp_resource_ref(video_capture); // prevents freeing while thread is still running
    pthread_create(&vc->thread, NULL, video_capture_thread, vc);
    vc->thread_started = 1;

    vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture,
                                        PP_VIDEO_CAPTURE_STATUS_STARTED);

done:
    pp_resource_release(video_capture);
    return PP_OK;
}

int32_t
ppb_video_capture_reuse_buffer(PP_Resource video_capture, uint32_t buffer)
{
    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (buffer < vc->buffer_count)
        vc->buffer_is_free[buffer] = 1;

    pp_resource_release(video_capture);
    return PP_OK;
}

int32_t
ppb_video_capture_stop_capture(PP_Resource video_capture)
{
    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, bad resource\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    if (!vc->thread_started)
        goto done;

    vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture,
                                        PP_VIDEO_CAPTURE_STATUS_STOPPING);

    vc->terminate_thread = 1;
    pthread_t thread = vc->thread;

    pp_resource_release(video_capture);

    pthread_join(thread, NULL);

    vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, resource gone\n", __func__);
        return PP_ERROR_BADRESOURCE;
    }

    vc->thread_started = 0;
    vc->terminate_thread = 0;
    vc->ppp_video_capture_dev->OnStatus(vc->instance->id, video_capture,
                                        PP_VIDEO_CAPTURE_STATUS_STOPPED);

    pp_resource_unref(video_capture);   // remove reference made in start_capture()

done:
    pp_resource_release(video_capture);
    return PP_OK;
}

void
ppb_video_capture_close(PP_Resource video_capture)
{
    ppb_video_capture_stop_capture(video_capture);

    struct pp_video_capture_s *vc = pp_resource_acquire(video_capture, PP_RESOURCE_VIDEO_CAPTURE);
    if (!vc) {
        trace_error("%s, bad resource\n", __func__);
        return;
    }

    ppb_video_capture_destroy(vc);

    pp_resource_release(video_capture);
    return;
}


// trace wrappers
TRACE_WRAPPER
PP_Resource
trace_ppb_video_capture_create(PP_Instance instance)
{
    trace_info("[PPB] {full} %s instance=%d\n", __func__+6, instance);
    return ppb_video_capture_create(instance);
}