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); } }
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; }
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; }
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); }
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; }
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 } }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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); }
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; }
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; }
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; }
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); }
// // 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); }
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; }
/** 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; } }
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; }
/** 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); }