/** * Attach the current thread to the UDP Socket * * @param us UDP Socket * * @return 0 if success, otherwise errorcode */ int udp_thread_attach(struct udp_sock *us) { int err = 0; if (!us) return EINVAL; if (-1 != us->fd) { err = fd_listen(us->fd, FD_READ, udp_read_handler, us); if (err) goto out; } if (-1 != us->fd6) { err = fd_listen(us->fd6, FD_READ, udp_read_handler6, us); if (err) goto out; } out: if (err) udp_thread_detach(us); return err; }
static int enqueue(struct tcp_conn *tc, struct mbuf *mb) { const size_t n = mbuf_get_left(mb); struct tcp_qent *qe; int err; if (tc->txqsz + n > tc->txqsz_max) return ENOSPC; if (!tc->sendq.head && !tc->sendh) { err = fd_listen(tc->fdc, FD_READ | FD_WRITE, tcp_recv_handler, tc); if (err) return err; } qe = mem_zalloc(sizeof(*qe), qent_destructor); if (!qe) return ENOMEM; list_append(&tc->sendq, &qe->le, qe); mbuf_init(&qe->mb); err = mbuf_write_mem(&qe->mb, mbuf_buf(mb), n); qe->mb.pos = 0; if (err) mem_deref(qe); else tc->txqsz += qe->mb.end; return err; }
/** * Accept an incoming TCP Connection * * @param tcp Returned TCP Connection object * @param ts Corresponding TCP Socket * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc; int err; if (!tcp || !ts || ts->fdc < 0) return EINVAL; tc = conn_alloc(eh, rh, ch, arg); if (!tc) return ENOMEM; /* Transfer ownership to TCP connection */ tc->fdc = ts->fdc; ts->fdc = -1; err = fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT, tcp_recv_handler, tc); if (err) { DEBUG_WARNING("accept: fd_listen(): %m\n", err); } if (err) mem_deref(tc); else *tcp = tc; return err; }
/*! @decl void create(int|string port, void|function accept_callback, @ *! void|string ip) *! @decl void create("stdin", void|function accept_callback) *! *! When called with an int or any string except @expr{"stdin"@} as *! first argument, this function does the same as @[bind()] would do *! with the same arguments. *! *! When called with @expr{"stdin"@} as argument, a socket is created *! out of the file descriptor 0. This is only useful if that actually *! IS a socket to begin with, and is equivalent to creating a port and *! initializing it with @[listen_fd](0). *! *! @seealso *! @[bind], @[listen_fd] */ static void port_create(INT32 args) { if(args) { if(TYPEOF(Pike_sp[-args]) == PIKE_T_INT || (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING && (Pike_sp[-args].u.string->len != 5 || strcmp("stdin",Pike_sp[-args].u.string->str)))) { port_bind(args); return; }else{ struct port *p = THIS; if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) SIMPLE_WRONG_NUM_ARGS_ERROR("create", 1); /* FIXME: Check that the argument is "stdin". */ do_close(p); change_fd_for_box (&p->box, 0); if(fd_listen(p->box.fd, 16384) < 0) { p->my_errno=errno; }else{ if(args > 1) assign_accept_cb (p, Pike_sp+1-args); } } } }
static int encode_update(struct videnc_state **vesp, const struct vidcodec *vc, struct videnc_param *prm, const char *fmtp, videnc_packet_h *pkth, void *arg) { struct videnc_state *st; int err = 0; (void)fmtp; if (!vesp || !vc || !prm || !pkth) return EINVAL; if (*vesp) return 0; st = mem_zalloc(sizeof(*st), enc_destructor); if (!st) return ENOMEM; st->encprm = *prm; st->pkth = pkth; st->arg = arg; st->fd = open(v4l2.device, O_RDWR); if (st->fd == -1) { err = errno; warning("Opening video device (%m)\n", err); goto out; } err = print_caps(st->fd); if (err) goto out; err = init_mmap(st, st->fd); if (err) goto out; err = query_buffer(st->fd); if (err) goto out; err = start_streaming(st->fd); if (err) goto out; err = fd_listen(st->fd, FD_READ, read_handler, st); if (err) goto out; info("v4l2_codec: video encoder %s: %d fps, %d bit/s, pktsize=%u\n", vc->name, prm->fps, prm->bitrate, prm->pktsize); out: if (err) mem_deref(st); else *vesp = st; return err; }
/*! @decl int listen_fd(int fd, void|function accept_callback) *! *! This function does the same as @[bind], except that instead of *! creating a new socket and bind it to a port, it expects the file *! descriptor @[fd] to be an already open port. *! *! @note *! This function is only for the advanced user, and is generally used *! when sockets are passed to Pike at exec time. *! *! @seealso *! @[bind], @[accept] */ static void port_listen_fd(INT32 args) { struct port *p = THIS; struct svalue *cb = NULL; int fd; do_close(p); get_all_args(NULL, args, "%d.%*", &fd, &cb); if(fd<0) { errno = p->my_errno=EBADF; pop_n_elems(args); push_int(0); return; } if(fd_listen(fd, 16384) < 0) { p->my_errno=errno; pop_n_elems(args); push_int(0); return; } change_fd_for_box (&p->box, fd); if(cb) assign_accept_cb (p, cb); p->my_errno=0; pop_n_elems(args); push_int(1); }
static int ui_alloc(struct ui_st **stp) { struct ui_st *st; int err; if (!stp) return EINVAL; st = mem_zalloc(sizeof(*st), ui_destructor); if (!st) return ENOMEM; tmr_init(&st->tmr); err = fd_listen(STDIN_FILENO, FD_READ, ui_fd_handler, st); if (err) goto out; err = term_setup(st); if (err) { info("stdio: could not setup terminal: %m\n", err); err = 0; } out: if (err) mem_deref(st); else *stp = st; return err; }
int sock_listen(net_sock_t *nsock, int max_pending) { network_sock_t *sock = (network_sock_t *)nsock; if (sock == NULL) return(1); return(fd_listen(sock->fd, max_pending)); }
static int src_alloc(struct ausrc_st **stp, struct ausrc *as, struct media_ctx **ctx, struct ausrc_prm *prm, const char *device, ausrc_read_h *rh, ausrc_error_h *errh, void *arg) { struct ausrc_st *st; int err; (void)ctx; (void)errh; st = mem_zalloc(sizeof(*st), ausrc_destructor); if (!st) return ENOMEM; st->fd = -1; st->rh = rh; st->errh = errh; st->arg = arg; if (!device) device = oss_dev; prm->fmt = AUFMT_S16LE; st->mb = mbuf_alloc(2 * prm->frame_size); if (!st->mb) { err = ENOMEM; goto out; } st->fd = open(device, O_RDONLY); if (st->fd < 0) { err = errno; goto out; } err = fd_listen(st->fd, FD_READ, read_handler, st); if (err) goto out; err = oss_reset(st->fd, prm->srate, prm->ch, prm->frame_size, 1); if (err) goto out; st->as = mem_ref(as); out: if (err) mem_deref(st); else *stp = st; return err; }
/** * Set the send handler on a TCP Connection, which will be called * every time it is ready to send data * * @param tc TCP Connection * @param sendh TCP Send handler * * @return 0 if success, otherwise errorcode */ int tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh) { if (!tc) return EINVAL; tc->sendh = sendh; if (tc->sendq.head || !sendh) return 0; return fd_listen(tc->fdc, FD_READ | FD_WRITE, tcp_recv_handler, tc); }
/** * Listen on a TCP Socket * * @param ts TCP Socket * @param backlog Maximum length the queue of pending connections * * @return 0 if success, otherwise errorcode */ int tcp_sock_listen(struct tcp_sock *ts, int backlog) { int err; if (!ts) return EINVAL; if (ts->fd < 0) { DEBUG_WARNING("sock_listen: invalid fd\n"); return EBADF; } if (listen(ts->fd, backlog) < 0) { err = errno; DEBUG_WARNING("sock_listen: listen(): %m\n", err); return err; } return fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts); }
/** * Allocate a new Message Queue * * @param mqp Pointer to allocated Message Queue * @param h Message handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg) { struct mqueue *mq; int err = 0; if (!mqp || !h) return EINVAL; mq = mem_zalloc(sizeof(*mq), destructor); if (!mq) return ENOMEM; mq->h = h; mq->arg = arg; mq->pfd[0] = mq->pfd[1] = -1; if (pipe(mq->pfd) < 0) { err = errno; goto out; } err = net_sockopt_blocking_set(mq->pfd[0], false); if (err) goto out; err = net_sockopt_blocking_set(mq->pfd[1], false); if (err) goto out; err = fd_listen(mq->pfd[0], FD_READ, event_handler, mq); if (err) goto out; out: if (err) mem_deref(mq); else *mqp = mq; return err; }
/** * Handler for incoming TCP connections. * * @param flags Event flags. * @param arg Handler argument. */ static void tcp_conn_handler(int flags, void *arg) { struct sa peer; struct tcp_sock *ts = arg; int err; (void)flags; sa_init(&peer, AF_UNSPEC); if (ts->fdc >= 0) (void)close(ts->fdc); ts->fdc = SOK_CAST accept(ts->fd, &peer.u.sa, &peer.len); if (-1 == ts->fdc) { #if TARGET_OS_IPHONE if (EAGAIN == errno) { struct tcp_sock *ts_new; struct sa laddr; err = tcp_sock_local_get(ts, &laddr); if (err) return; if (ts->fd >= 0) { fd_close(ts->fd); (void)close(ts->fd); ts->fd = -1; } err = tcp_listen(&ts_new, &laddr, NULL, NULL); if (err) return; ts->fd = ts_new->fd; ts_new->fd = -1; mem_deref(ts_new); fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts); } #endif return; } err = net_sockopt_blocking_set(ts->fdc, false); if (err) { DEBUG_WARNING("conn handler: nonblock set: %m\n", err); (void)close(ts->fdc); ts->fdc = -1; return; } tcp_sockopt_set(ts->fdc); if (ts->connh) ts->connh(&peer, ts->arg); }
/** * Stop listening for events on a file descriptor * * @param fd File descriptor */ void fd_close(int fd) { (void)fd_listen(fd, 0, NULL, NULL); }
/*! @decl int bind(int|string port, void|function accept_callback, @ *! void|string ip, void|string reuse_port) *! *! Opens a socket and binds it to port number on the local machine. *! If the second argument is present, the socket is set to *! nonblocking and the callback funcition is called whenever *! something connects to it. The callback will receive the id for *! this port as argument and should typically call @[accept] to *! establish a connection. *! *! If the optional argument @[ip] is given, @[bind] will try to bind *! to an interface with that host name or IP number. Omitting this *! will bind to all available IPv4 addresses; specifying "::" will *! bind to all IPv4 and IPv6 addresses. *! *! If the OS supports TCP_FASTOPEN it is enabled automatically. *! *! If the OS supports SO_REUSEPORT it is enabled if the fourth argument is true. *! *! @returns *! 1 is returned on success, zero on failure. @[errno] provides *! further details about the error in the latter case. *! *! @seealso *! @[accept], @[set_id] */ static void port_bind(INT32 args) { struct port *p = THIS; PIKE_SOCKADDR addr; int addr_len,fd,tmp; do_close(p); if(args < 1) SIMPLE_WRONG_NUM_ARGS_ERROR("bind", 1); if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT && (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING || Pike_sp[-args].u.string->size_shift)) SIMPLE_ARG_TYPE_ERROR("bind", 1, "int|string(8bit)"); addr_len = get_inet_addr(&addr, (args > 2 && TYPEOF(Pike_sp[2-args])==PIKE_T_STRING? Pike_sp[2-args].u.string->str : NULL), (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING? Pike_sp[-args].u.string->str : NULL), (TYPEOF(Pike_sp[-args]) == PIKE_T_INT? Pike_sp[-args].u.integer : -1), 0); INVALIDATE_CURRENT_TIME(); fd=fd_socket(SOCKADDR_FAMILY(addr), SOCK_STREAM, 0); if(fd < 0) { p->my_errno=errno; pop_n_elems(args); push_int(0); return; } #ifdef SO_REUSEPORT if( args > 3 && Pike_sp[3-args].u.integer ) { /* FreeBSD 7.x wants this to reuse portnumbers. * Linux 2.6.x seems to have reserved a slot for the option, but not * enabled it. Survive libc's with the option on kernels without. * * The emulated Linux runtime on MS Windows 10 fails this with EINVAL. */ int o=1; if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0) #ifdef ENOPROTOOPT && (errno != ENOPROTOOPT) #endif #ifdef EINVAL && (errno != EINVAL) #endif #ifdef WSAENOPROTOOPT && (errno != WSAENOPROTOOPT) #endif ){ p->my_errno=errno; while (fd_close(fd) && errno == EINTR) {} errno = p->my_errno; pop_n_elems(args); push_int(0); return; } } #endif #ifndef __NT__ { int o=1; if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0) { p->my_errno=errno; while (fd_close(fd) && errno == EINTR) {} errno = p->my_errno; pop_n_elems(args); push_int(0); return; } } #endif #if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6) if (SOCKADDR_FAMILY(addr) == AF_INET6) { /* Attempt to enable dual-stack (ie mapped IPv4 adresses). * Needed on WIN32. * cf http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx */ int o = 0; fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int)); } #endif my_set_close_on_exec(fd,1); THREADS_ALLOW_UID(); if( !(tmp=fd_bind(fd, (struct sockaddr *)&addr, addr_len) < 0) ) #ifdef TCP_FASTOPEN tmp = 256, setsockopt(fd,SOL_TCP, TCP_FASTOPEN, &tmp, sizeof(tmp)), #endif (tmp = fd_listen(fd, 16384) < 0); THREADS_DISALLOW_UID(); if(!Pike_fp->current_object->prog) { if (fd >= 0) while (fd_close(fd) && errno == EINTR) {} Pike_error("Object destructed in Stdio.Port->bind()\n"); } if(tmp) { p->my_errno=errno; while (fd_close(fd) && errno == EINTR) {} errno = p->my_errno; pop_n_elems(args); push_int(0); return; } change_fd_for_box (&p->box, fd); if(args > 1) assign_accept_cb (p, Pike_sp+1-args); p->my_errno=0; pop_n_elems(args); push_int(1); }
/*! @decl int bind_unix(string path, void|function accept_callback) *! *! Opens a Unix domain socket at the given path in the file system. *! If the second argument is present, the socket is set to *! nonblocking and the callback funcition is called whenever *! something connects to it. The callback will receive the id for *! this port as argument and should typically call @[accept] to *! establish a connection. *! *! @returns *! 1 is returned on success, zero on failure. @[errno] provides *! further details about the error in the latter case. *! *! @note *! This function is only available on systems that support Unix domain *! sockets. *! *! @note *! @[path] had a quite restrictive length limit (~100 characters) *! prior to Pike 7.8.334. *! *! @seealso *! @[accept], @[set_id] */ static void bind_unix(INT32 args) { struct port *p = THIS; struct sockaddr_un *addr; struct pike_string *path; struct svalue *cb = NULL; int addr_len,fd,tmp; do_close(p); get_all_args(NULL, args, "%n.%*", &path, &cb); /* NOTE: Some operating systems (eg Linux 2.6) do not support * paths longer than what fits into a plain struct sockaddr_un. */ addr_len = sizeof(struct sockaddr_un) + path->len + 1 - sizeof(addr->sun_path); addr = xalloc(addr_len); strcpy(addr->sun_path, path->str); addr->sun_family = AF_UNIX; #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN /* Length including NUL. */ addr->sun_len = path->len + 1; #endif fd=fd_socket(AF_UNIX, SOCK_STREAM, 0); if(fd < 0) { free(addr); p->my_errno=errno; pop_n_elems(args); push_int(0); return; } #ifndef __NT__ { int o=1; do { tmp = fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)); } while ((tmp < 0) && (errno == EINTR)); } #endif my_set_close_on_exec(fd,1); THREADS_ALLOW_UID(); do { tmp = fd_bind(fd, (struct sockaddr *)addr, addr_len); } while ((tmp < 0) && (errno == EINTR)); if (tmp >= 0) { do { tmp = fd_listen(fd, 16384); } while ((tmp < 0) && (errno == EINTR)); } THREADS_DISALLOW_UID(); free(addr); if(!Pike_fp->current_object->prog) { if (fd >= 0) while (fd_close(fd) && errno == EINTR) {} Pike_error("Object destructed in Stdio.Port->bind_unix()\n"); } if(tmp < 0) { p->my_errno=errno; while (fd_close(fd) && errno == EINTR) {} errno = p->my_errno; pop_n_elems(args); push_int(0); return; } change_fd_for_box (&p->box, fd); if (cb) assign_accept_cb (p, cb); p->my_errno=0; pop_n_elems(args); push_int(1); }
/** * Connect to a remote peer * * @param tc TCP Connection object * @param peer Network address of peer * * @return 0 if success, otherwise errorcode */ int tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer) { struct addrinfo hints, *res = NULL, *r; char addr[64]; char serv[NI_MAXSERV]; int error, err = 0; if (!tc || !sa_isset(peer, SA_ALL)) return EINVAL; tc->active = true; if (tc->fdc < 0) { DEBUG_WARNING("invalid fd\n"); return EBADF; } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, peer); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(peer)); error = getaddrinfo(addr, serv, &hints, &res); if (error) { DEBUG_WARNING("connect: getaddrinfo(): (%s)\n", gai_strerror(error)); return EADDRNOTAVAIL; } for (r = res; r; r = r->ai_next) { struct sockaddr *sa = r->ai_addr; again: if (0 == connect(tc->fdc, sa, SIZ_CAST r->ai_addrlen)) { err = 0; goto out; } else { #ifdef WIN32 /* Special error handling for Windows */ if (WSAEWOULDBLOCK == WSAGetLastError()) { err = 0; goto out; } #endif /* Special case for mingw32/wine */ if (0 == errno) { err = 0; goto out; } if (EINTR == errno) goto again; if (EINPROGRESS != errno && EALREADY != errno) { err = errno; DEBUG_INFO("connect: connect() %J: %m\n", peer, err); } } } out: freeaddrinfo(res); if (err) return err; return fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT, tcp_recv_handler, tc); }
static void tcp_recv_handler(int flags, void *arg) { struct tcp_conn *tc = arg; struct mbuf *mb = NULL; bool hlp_estab = false; struct le *le; ssize_t n; int err; socklen_t err_len = sizeof(err); if (flags & FD_EXCEPT) { DEBUG_INFO("recv handler: got FD_EXCEPT on fd=%d\n", tc->fdc); } /* check for any errors */ if (-1 == getsockopt(tc->fdc, SOL_SOCKET, SO_ERROR, BUF_CAST &err, &err_len)) { DEBUG_WARNING("recv handler: getsockopt: (%m)\n", errno); return; } if (err) { conn_close(tc, err); return; } #if 0 if (EINPROGRESS != err && EALREADY != err) { DEBUG_WARNING("recv handler: Socket error (%m)\n", err); return; } #endif if (flags & FD_WRITE) { if (tc->connected) { uint32_t nrefs; mem_ref(tc); err = dequeue(tc); nrefs = mem_nrefs(tc); mem_deref(tc); /* check if connection was deref'd from send handler */ if (nrefs == 1) return; if (err) { conn_close(tc, err); return; } if (!tc->sendq.head && !tc->sendh) { err = fd_listen(tc->fdc, FD_READ, tcp_recv_handler, tc); if (err) { conn_close(tc, err); return; } } if (flags & FD_READ) goto read; return; } tc->connected = true; err = fd_listen(tc->fdc, FD_READ, tcp_recv_handler, tc); if (err) { DEBUG_WARNING("recv handler: fd_listen(): %m\n", err); conn_close(tc, err); return; } le = tc->helpers.head; while (le) { struct tcp_helper *th = le->data; le = le->next; if (th->estabh(&err, tc->active, th->arg) || err) { if (err) conn_close(tc, err); return; } } if (tc->estabh) tc->estabh(tc->arg); return; } read: mb = mbuf_alloc(tc->rxsz); if (!mb) return; n = recv(tc->fdc, BUF_CAST mb->buf, mb->size, 0); if (0 == n) { mem_deref(mb); conn_close(tc, 0); return; } else if (n < 0) { DEBUG_WARNING("recv handler: recv(): %m\n", errno); goto out; } mb->end = n; le = tc->helpers.head; while (le) { struct tcp_helper *th = le->data; bool hdld = false; le = le->next; if (hlp_estab) { hdld |= th->estabh(&err, tc->active, th->arg); if (err) { conn_close(tc, err); goto out; } } if (mb->pos < mb->end) { hdld |= th->recvh(&err, mb, &hlp_estab, th->arg); if (err) { conn_close(tc, err); goto out; } } if (hdld) goto out; } mbuf_trim(mb); if (hlp_estab && tc->estabh) { uint32_t nrefs; mem_ref(tc); tc->estabh(tc->arg); nrefs = mem_nrefs(tc); mem_deref(tc); /* check if connection was deref'ed from establish handler */ if (nrefs == 1) goto out; } if (mb->pos < mb->end && tc->recvh) { tc->recvh(mb, tc->arg); } out: mem_deref(mb); }