static int server_main(pid_t ppid, const char *sockpath) { struct sockaddr_storage name, storage; struct sockaddr_un *addr; socklen_t namelen; int error, level, optname, optval, s, sock; sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock == -1) return (1); level = SOL_SOCKET; optname = SO_REUSEADDR; optval = 1; if (setsockopt(sock, level, optname, &optval, sizeof(optval)) == -1) return (2); addr = (struct sockaddr_un *)&storage; addr->sun_family = AF_LOCAL; strcpy(addr->sun_path, sockpath); addr->sun_len = SUN_LEN(addr); if (bind(sock, (struct sockaddr *)addr, addr->sun_len) == -1) return (3); if (listen(sock, 0) == -1) return (4); if (kill(ppid, SIG) == -1) return (5); namelen = sizeof(name); if ((s = accept(sock, (struct sockaddr *)&name, &namelen)) == -1) return (6); if (do_recvmsg(s) != 0) return (7); if (close(s) == -1) return (8); if (close(sock) == -1) return (9); if (unlink(sockpath) == -1) return (10); return (0); }
static int slave(slave_data_init_hnd data_init_hnd, int sock) { sigset_t sset; struct sigaction sa; struct timeval client_done, client_start, new_client_duration = { NEW_CLIENT_DURATION, 0 }; void *clnt_data = NULL; int conn = -1, srv = -1, req_cnt = 0, sockflags, h_errno, pid, i, first_request = 0; /* 1 -> first request from connected client expected */ if ( (pid = fork()) ) return pid; #ifdef LB_PROF monstartup((u_long)&_start, (u_long)&etext); #endif srandom(getpid()+time(NULL)); for ( i = 0; i < services_ct; i++ ) close(services[i].conn); sigemptyset(&sset); sigaddset(&sset, SIGTERM); sigaddset(&sset, SIGINT); sigaddset(&sset, SIGUSR1); memset(&sa, 0, sizeof(sa)); sa.sa_handler = catchsig; sigaction(SIGUSR1, &sa, NULL); if ( (sockflags = fcntl(sock, F_GETFL, 0)) < 0 || fcntl(sock, F_SETFL, sockflags | O_NONBLOCK) < 0 ) { dprintf(("[%d] fcntl(master_sock): %s\n", getpid(), strerror(errno))); if ( !debug ) syslog(LOG_CRIT, "fcntl(master_sock): %m"); exit(1); } if ( data_init_hnd && data_init_hnd(&clnt_data) ) /* * XXX: what if the error remains and master will start new slave * again and again? * * Then we are in a deep shit. */ exit(1); while ( !die && (req_cnt < set_slave_reqs_max || (conn >= 0 && first_request))) { fd_set fds; int max = sock, connflags, newconn = -1, newsrv = -1; enum { KICK_DONT = 0, KICK_IDLE, KICK_LOAD, KICK_HANDLER, KICK_COUNT } kick_client = KICK_DONT; static char * kicks[] = { "don't kick", "idle client", "high load", "no request handler", "request count limit reached", }; unsigned long seq; struct timeval now,to; FD_ZERO(&fds); FD_SET(sock, &fds); if ( conn >= 0 ) FD_SET(conn, &fds); if ( conn > sock ) max = conn; to = set_idle_to; sigprocmask(SIG_UNBLOCK, &sset, NULL); switch (select(max+1, &fds, NULL, NULL, to.tv_sec >= 0 ? &to : NULL)) { case -1: if ( errno != EINTR ) { dprintf(("[%d] select(): %s\n", getpid(), strerror(errno))); if ( !debug ) syslog(LOG_CRIT, "select(): %m"); exit(1); } continue; case 0: if ( conn < 0 ) continue; default: break; } sigprocmask(SIG_BLOCK, &sset, NULL); gettimeofday(&now,NULL); if ( conn >= 0 && FD_ISSET(conn, &fds) ) { /* * serve the request */ int rv; dprintf(("[%d] incoming request\n", getpid())); if ( !services[srv].on_request_hnd ) { kick_client = KICK_HANDLER; } else { first_request = 0; to = set_request_to; if ((rv = services[srv].on_request_hnd(conn,to.tv_sec>=0 ? &to : NULL,clnt_data)) == ENOTCONN) { if (services[srv].on_disconnect_hnd && (rv = services[srv].on_disconnect_hnd(conn,NULL,clnt_data))) { dprintf(("[%d] disconnect handler: %s, terminating\n",getpid(),strerror(rv))); exit(1); } close(conn); conn = -1; srv = -1; dprintf(("[%d] Connection closed\n", getpid())); } else if (rv > 0) { /* non-fatal error -> close connection and contiue * XXX: likely to leak resources but can we call on_disconnect_hnd() on error? */ close(conn); conn = -1; srv = -1; dprintf(("[%d] %s, connection closed\n",getpid(),strerror(rv))); continue; } else if ( rv < 0 ) { /* unknown error -> clasified as FATAL -> kill slave */ dprintf(("[%d] %s, terminating\n",getpid(),strerror(-rv))); exit(1); } else { dprintf(("[%d] request done\n", getpid())); gettimeofday(&client_done, NULL); } if (!check_timeout(new_client_duration,client_start,now)) continue; } } else { if (conn >= 0 && check_timeout(set_idle_to, client_done, now)) kick_client = KICK_IDLE; } if ( (conn < 0 || !first_request) && FD_ISSET(sock, &fds) && req_cnt < set_slave_reqs_max ) { /* Prefer slaves with no connection, then kick idle clients, * active ones last. Wait less if we have serviced a request in the meantime. * Tuned for HZ=100 timer. */ if ( conn >= 0 ) usleep( kick_client || FD_ISSET(conn, &fds) ? 11000 : 21000); if ( do_recvmsg(sock, &newconn, &seq, &newsrv) ) switch ( errno ) { case EINTR: /* XXX: signals are blocked */ case EAGAIN: continue; default: dprintf(("[%d] recvmsg(): %s\n", getpid(), strerror(errno))); if (!debug) syslog(LOG_CRIT,"recvmsg(): %m\n"); exit(1); } kick_client = KICK_LOAD; } if (req_cnt >= set_slave_reqs_max && !first_request) kick_client = KICK_COUNT; if ( kick_client && conn >= 0 ) { if ( services[srv].on_disconnect_hnd ) services[srv].on_disconnect_hnd(conn, NULL, clnt_data); close(conn); conn = -1; srv = -1; dprintf(("[%d] Connection closed, %s\n", getpid(), kicks[kick_client])); } if ( newconn >= 0 ) { int ret; conn = newconn; srv = newsrv; gettimeofday(&client_start, NULL); switch ( send(sock, &seq, sizeof(seq), 0) ) { case -1: if (debug) perror("send()"); else syslog(LOG_CRIT, "send(): %m\n"); exit(1); case sizeof(seq): break; default: dprintf(("[%d] send(): incomplete message\n", getpid())); exit(1); } req_cnt++; dprintf(("[%d] serving %s connection %lu\n", getpid(), services[srv].id? services[srv].id: "", seq)); connflags = fcntl(conn, F_GETFL, 0); if ( fcntl(conn, F_SETFL, connflags | O_NONBLOCK) < 0 ) { dprintf(("[%d] can't set O_NONBLOCK mode (%s), closing.\n", getpid(), strerror(errno))); if ( !debug ) syslog(LOG_ERR, "can't set O_NONBLOCK mode (%s), closing.\n", strerror(errno)); close(conn); conn = srv = -1; continue; } to = set_connect_to; if ( services[srv].on_new_conn_hnd && (ret = services[srv].on_new_conn_hnd(conn, to.tv_sec >= 0 ? &to : NULL, clnt_data)) ) { dprintf(("[%d] Connection not estabilished, err = %d.\n", getpid(),ret)); if ( !debug ) syslog(LOG_ERR, "Connection not estabilished, err = %d.\n",ret); close(conn); conn = srv = -1; if (ret < 0) exit(1); continue; } gettimeofday(&client_done, NULL); first_request = 1; } } if ( die ) { dprintf(("[%d] Terminating on signal %d\n", getpid(), die)); if ( !debug ) syslog(LOG_INFO, "Terminating on signal %d", die); } if (conn >= 0 && services[srv].on_disconnect_hnd ) services[srv].on_disconnect_hnd(conn, NULL, clnt_data); dprintf(("[%d] Terminating after %d requests\n", getpid(), req_cnt)); if ( !debug ) syslog(LOG_INFO, "Terminating after %d requests", req_cnt); exit(0); }
PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out) { int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_ioctl() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* track the system call we are performing in case it gets cancelled */ uds_fd_table[minor].call_nr = dev_m_in->m_type; uds_fd_table[minor].ioctl = dev_m_in->COUNT; uds_fd_table[minor].syscall_done = 0; /* setup select(2) framework */ uds_fd_table[minor].selecting = 0; /* update the owner endpoint - yes it's really stored in POSITION */ uds_fd_table[minor].owner = dev_m_in->POSITION; switch (dev_m_in->COUNT) { /* Handle the ioctl(2) command */ case NWIOSUDSCONN: /* connect to a listening socket -- connect() */ return do_connect(dev_m_in, dev_m_out); case NWIOSUDSACCEPT: /* accept an incoming connection -- accept() */ return do_accept(dev_m_in, dev_m_out); case NWIOSUDSBLOG: /* set the backlog_size and put the socket into the * listening state -- listen() */ return do_listen(dev_m_in, dev_m_out); case NWIOSUDSTYPE: /* set the type for this socket (i.e. * SOCK_STREAM, SOCK_DGRAM, etc) -- socket() */ return do_socket(dev_m_in, dev_m_out); case NWIOSUDSADDR: /* set the address for this socket -- bind() */ return do_bind(dev_m_in, dev_m_out); case NWIOGUDSADDR: /* get the address for this socket -- getsockname() */ return do_getsockname(dev_m_in, dev_m_out); case NWIOGUDSPADDR: /* get the address for the peer -- getpeername() */ return do_getpeername(dev_m_in, dev_m_out); case NWIOSUDSSHUT: /* shutdown a socket for reading, writing, or * both -- shutdown() */ return do_shutdown(dev_m_in, dev_m_out); case NWIOSUDSPAIR: /* connect two sockets -- socketpair() */ return do_socketpair(dev_m_in, dev_m_out); case NWIOGUDSSOTYPE: /* get socket type -- getsockopt(SO_TYPE) */ return do_getsockopt_sotype(dev_m_in, dev_m_out); case NWIOGUDSPEERCRED: /* get peer endpoint -- getsockopt(SO_PEERCRED) */ return do_getsockopt_peercred(dev_m_in, dev_m_out); case NWIOSUDSTADDR: /* set target address -- sendto() */ return do_sendto(dev_m_in, dev_m_out); case NWIOGUDSFADDR: /* get from address -- recvfrom() */ return do_recvfrom(dev_m_in, dev_m_out); case NWIOGUDSSNDBUF: /* get the send buffer size -- getsockopt(SO_SNDBUF) */ return do_getsockopt_sndbuf(dev_m_in, dev_m_out); case NWIOSUDSSNDBUF: /* set the send buffer size -- setsockopt(SO_SNDBUF) */ return do_setsockopt_sndbuf(dev_m_in, dev_m_out); case NWIOGUDSRCVBUF: /* get the send buffer size -- getsockopt(SO_SNDBUF) */ return do_getsockopt_rcvbuf(dev_m_in, dev_m_out); case NWIOSUDSRCVBUF: /* set the send buffer size -- setsockopt(SO_SNDBUF) */ return do_setsockopt_rcvbuf(dev_m_in, dev_m_out); case NWIOSUDSCTRL: /* set the control data -- sendmsg() */ return do_sendmsg(dev_m_in, dev_m_out); case NWIOGUDSCTRL: /* set the control data -- recvmsg() */ return do_recvmsg(dev_m_in, dev_m_out); default: /* the IOCTL command is not valid for /dev/uds -- * this happens a lot and is normal. a lot of * libc functions determine the socket type with * IOCTLs. Any not for us simply get a EBADIOCTL * response. */ uds_fd_table[minor].syscall_done = 1; uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EBADIOCTL); return EBADIOCTL; } }