/* * Returns 1, 0 or -1 * In nonblocking mode, you must call again with same buffer while * return value is 1. */ static int unix_put(COMSTACK h, char *buf, int size) { int res; struct unix_state *state = (struct unix_state *)h->cprivate; TRC(fprintf(stderr, "unix_put: size=%d\n", size)); h->io_pending = 0; h->event = CS_DATA; if (state->towrite < 0) { state->towrite = size; state->written = 0; } else if (state->towrite != size) { h->cerrno = CSWRONGBUF; return -1; } while (state->towrite > state->written) { if ((res = send(h->iofile, buf + state->written, size - state->written, #ifdef MSG_NOSIGNAL MSG_NOSIGNAL #else 0 #endif )) < 0) { if ( yaz_errno() == EWOULDBLOCK #ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN #endif #endif ) { TRC(fprintf(stderr, " Flow control stop\n")); h->io_pending = CS_WANT_WRITE; return 1; } h->cerrno = CSYSERR; return -1; } state->written += res; TRC(fprintf(stderr, " Wrote %d, written=%d, nbytes=%d\n", res, state->written, size)); } state->towrite = state->written = -1; TRC(fprintf(stderr, " Ok\n")); return 0; }
/* * connect(2) will block (sometimes) - nothing we can do short of doing * weird things like spawning subprocesses or threading or some weird junk * like that. */ static int unix_connect(COMSTACK h, void *address) { struct sockaddr_un *add = (struct sockaddr_un *)address; int r; int i; TRC(fprintf(stderr, "unix_connect\n")); h->io_pending = 0; if (h->state != CS_ST_UNBND) { h->cerrno = CSOUTSTATE; return -1; } for (i = 0; i<3; i++) { r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add)); if (r < 0 && yaz_errno() == EAGAIN) { #if HAVE_USLEEP usleep(i*10000+1000); /* 1ms, 11ms, 21ms */ #else sleep(1); #endif continue; } else break; } if (r < 0) { if (yaz_errno() == EINPROGRESS) { h->event = CS_CONNECT; h->state = CS_ST_CONNECTING; h->io_pending = CS_WANT_WRITE; return 1; } h->cerrno = CSYSERR; return -1; } h->event = CS_CONNECT; h->state = CS_ST_CONNECTING; return unix_rcvconnect (h); }
void yaz_strerror(char *buf, size_t bufsz) { #ifdef WIN32 DWORD err; #endif char *cp; #ifdef WIN32 err = GetLastError(); if (err) { FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default lang */ (LPTSTR) buf, bufsz-1, NULL); } else *buf = '\0'; #else /* UNIX */ #if HAVE_STRERROR_R *buf = '\0'; strerror_r(errno, buf, bufsz); /* if buffer is unset - use strerror anyway (GLIBC bug) */ if (*buf == '\0') strcpy(buf, strerror(yaz_errno())); #else strcpy(buf, strerror(yaz_errno())); #endif /* UNIX */ #endif if ((cp = strrchr(buf, '\n'))) *cp = '\0'; if ((cp = strrchr(buf, '\r'))) *cp = '\0'; }
static int unix_listen(COMSTACK h, char *raddr, int *addrlen, int (*check_ip)(void *cd, const char *a, int len, int t), void *cd) { struct sockaddr_un addr; YAZ_SOCKLEN_T len = sizeof(addr); TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid())); if (h->state != CS_ST_IDLE) { h->cerrno = CSOUTSTATE; return -1; } h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len); if (h->newfd < 0) { if ( yaz_errno() == EWOULDBLOCK #ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN #endif #endif ) h->cerrno = CSNODATA; else h->cerrno = CSYSERR; return -1; } if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un)) memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un)); else if (addrlen) *addrlen = 0; h->state = CS_ST_INCON; return 0; }
/* * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer, * 0=connection closed. */ static int unix_get(COMSTACK h, char **buf, int *bufsize) { unix_state *sp = (unix_state *)h->cprivate; char *tmpc; int tmpi, berlen, rest, req, tomove; int hasread = 0, res; TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize)); if (sp->altlen) /* switch buffers */ { TRC(fprintf(stderr, " %d bytes in altbuf (0x%x)\n", sp->altlen, (unsigned) sp->altbuf)); tmpc = *buf; tmpi = *bufsize; *buf = sp->altbuf; *bufsize = sp->altsize; hasread = sp->altlen; sp->altlen = 0; sp->altbuf = tmpc; sp->altsize = tmpi; } h->io_pending = 0; while (!(berlen = (*sp->complete)(*buf, hasread))) { if (!*bufsize) { if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK))) return -1; } else if (*bufsize - hasread < CS_UNIX_BUFCHUNK) if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2))) return -1; res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0); TRC(fprintf(stderr, " recv res=%d, hasread=%d\n", res, hasread)); if (res < 0) { if (yaz_errno() == EWOULDBLOCK #ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN #endif #endif || yaz_errno() == EINPROGRESS ) { h->io_pending = CS_WANT_READ; break; } else if (yaz_errno() == 0) continue; else return -1; } else if (!res) return hasread; hasread += res; } TRC (fprintf (stderr, " Out of read loop with hasread=%d, berlen=%d\n", hasread, berlen)); /* move surplus buffer (or everything if we didn't get a BER rec.) */ if (hasread > berlen) { tomove = req = hasread - berlen; rest = tomove % CS_UNIX_BUFCHUNK; if (rest) req += CS_UNIX_BUFCHUNK - rest; if (!sp->altbuf) { if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req))) return -1; } else if (sp->altsize < req) if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req))) return -1; TRC(fprintf(stderr, " Moving %d bytes to altbuf(0x%x)\n", tomove, (unsigned) sp->altbuf)); memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove); } if (berlen < CS_UNIX_BUFCHUNK - 1) *(*buf + berlen) = '\0'; return berlen ? berlen : 1; }
static int unix_bind(COMSTACK h, void *address, int mode) { unix_state *sp = (unix_state *)h->cprivate; struct sockaddr *addr = (struct sockaddr *)address; const char * path = ((struct sockaddr_un *)addr)->sun_path; struct stat stat_buf; TRC (fprintf (stderr, "unix_bind\n")); if(stat(path, &stat_buf) != -1) { struct sockaddr_un socket_unix; int socket_out = -1; if((stat_buf.st_mode&S_IFMT) != S_IFSOCK) { /* used to be S_ISSOCK */ h->cerrno = CSYSERR; yaz_set_errno(EEXIST); /* Not a socket (File exists) */ return -1; } if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { h->cerrno = CSYSERR; return -1; } socket_unix.sun_family = AF_UNIX; strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path)-1); socket_unix.sun_path[sizeof(socket_unix.sun_path)-1] = 0; if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) { if(yaz_errno() == ECONNREFUSED) { TRC (fprintf (stderr, "Socket exists but nobody is listening\n")); } else { h->cerrno = CSYSERR; return -1; } } else { close(socket_out); h->cerrno = CSYSERR; yaz_set_errno(EADDRINUSE); return -1; } unlink(path); } if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr))) { h->cerrno = CSYSERR; return -1; } if (chown(path, sp->uid, sp->gid)) { h->cerrno = CSYSERR; return -1; } if (chmod(path, sp->umask != -1 ? sp->umask : 0666)) { h->cerrno = CSYSERR; return -1; } if (mode == CS_SERVER && listen(h->iofile, 100) < 0) { h->cerrno = CSYSERR; return -1; } h->state = CS_ST_IDLE; h->event = CS_LISTEN; return 0; }
size_t yaz_iconv(yaz_iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { char *inbuf0 = 0; size_t r = 0; #if HAVE_ICONV_H if (cd->iconv_cd != (iconv_t) (-1)) { size_t r = iconv(cd->iconv_cd, inbuf, inbytesleft, outbuf, outbytesleft); if (r == (size_t)(-1)) { switch (yaz_errno()) { case E2BIG: cd->my_errno = YAZ_ICONV_E2BIG; break; case EINVAL: cd->my_errno = YAZ_ICONV_EINVAL; break; case EILSEQ: cd->my_errno = YAZ_ICONV_EILSEQ; break; default: cd->my_errno = YAZ_ICONV_UNKNOWN; } } return r; } #endif if (inbuf) inbuf0 = *inbuf; if (cd->init_flag) { cd->my_errno = YAZ_ICONV_UNKNOWN; if (cd->encoder.init_handle) (*cd->encoder.init_handle)(&cd->encoder); cd->unget_x = 0; cd->no_read_x = 0; if (cd->decoder.init_handle) { size_t no_read = 0; size_t r = (cd->decoder.init_handle)( cd, &cd->decoder, inbuf ? (unsigned char *) *inbuf : 0, inbytesleft ? *inbytesleft : 0, &no_read); if (r) { if (cd->my_errno == YAZ_ICONV_EINVAL) return r; cd->init_flag = 0; return r; } if (inbytesleft) *inbytesleft -= no_read; if (inbuf) *inbuf += no_read; } } cd->init_flag = 0; if (!inbuf || !*inbuf) { if (outbuf && *outbuf) { if (cd->unget_x) r = (*cd->encoder.write_handle)(cd, &cd->encoder, cd->unget_x, outbuf, outbytesleft); if (cd->encoder.flush_handle) r = (*cd->encoder.flush_handle)(cd, &cd->encoder, outbuf, outbytesleft); } if (r == 0) cd->init_flag = 1; cd->unget_x = 0; return r; } while (1) { unsigned long x; size_t no_read; if (cd->unget_x) { x = cd->unget_x; no_read = cd->no_read_x; } else { if (*inbytesleft == 0) { r = *inbuf - inbuf0; break; } x = (*cd->decoder.read_handle)( cd, &cd->decoder, (unsigned char *) *inbuf, *inbytesleft, &no_read); if (no_read == 0) { r = (size_t)(-1); break; } } if (x) { r = (*cd->encoder.write_handle)(cd, &cd->encoder, x, outbuf, outbytesleft); if (r) { /* unable to write it. save it because read_handle cannot rewind .. */ if (cd->my_errno == YAZ_ICONV_E2BIG) { cd->unget_x = x; cd->no_read_x = no_read; break; } } cd->unget_x = 0; } *inbytesleft -= no_read; (*inbuf) += no_read; } return r; }
int iochan_event_loop(IOCHAN *iochans) { do /* loop as long as there are active associations to process */ { IOCHAN p, nextp; int i; int tv_sec = 3600; int no_fds = 0; struct yaz_poll_fd *fds = 0; int res; time_t now = time(0); for (p = *iochans; p; p = p->next) no_fds++; fds = (struct yaz_poll_fd *) xmalloc(no_fds * sizeof(*fds)); for (i = 0, p = *iochans; p; p = p->next, i++) { time_t w, ftime; enum yaz_poll_mask input_mask = yaz_poll_none; yaz_log(log_level, "fd=%d flags=%d force_event=%d", p->fd, p->flags, p->force_event); if (p->force_event) tv_sec = 0; /* polling select */ if (p->flags & EVENT_INPUT) yaz_poll_add(input_mask, yaz_poll_read); if (p->flags & EVENT_OUTPUT) yaz_poll_add(input_mask, yaz_poll_write); if (p->flags & EVENT_EXCEPT) yaz_poll_add(input_mask, yaz_poll_except); if (p->max_idle && p->last_event) { ftime = p->last_event + p->max_idle; if (ftime < now) w = p->max_idle; else w = ftime - now; /* tv_sec will be minimum wait.. */ if (w < tv_sec) tv_sec = (int) w; /* can hold it because w < tv_sec */ } fds[i].fd = p->fd; fds[i].input_mask = input_mask; } res = yaz_poll(fds, no_fds, tv_sec, 0); if (res < 0) { if (yaz_errno() == EINTR) { xfree(fds); continue; } else { yaz_log(YLOG_WARN|YLOG_ERRNO, "yaz_poll"); xfree(fds); continue; } } now = time(0); for (i = 0, p = *iochans; p; p = p->next, i++) { int force_event = p->force_event; enum yaz_poll_mask output_mask = fds[i].output_mask; p->force_event = 0; if (!p->destroyed && ((output_mask & yaz_poll_read) || force_event == EVENT_INPUT)) { p->last_event = now; (*p->fun)(p, EVENT_INPUT); } if (!p->destroyed && ((output_mask & yaz_poll_write) || force_event == EVENT_OUTPUT)) { p->last_event = now; (*p->fun)(p, EVENT_OUTPUT); } if (!p->destroyed && ((output_mask & yaz_poll_except) || force_event == EVENT_EXCEPT)) { p->last_event = now; (*p->fun)(p, EVENT_EXCEPT); } if (!p->destroyed && ((p->max_idle && now - p->last_event >= p->max_idle) || force_event == EVENT_TIMEOUT)) { p->last_event = now; (*p->fun)(p, EVENT_TIMEOUT); } } xfree(fds); for (p = *iochans; p; p = nextp) { nextp = p->next; if (p->destroyed) { IOCHAN tmp = p, pr; /* We need to inform the threadlist that this channel has been destroyed */ statserv_remove(p); /* Now reset the pointers */ if (p == *iochans) *iochans = p->next; else { for (pr = *iochans; pr; pr = pr->next) if (pr->next == p) break; assert(pr); /* grave error if it weren't there */ pr->next = p->next; } if (nextp == p) nextp = p->next; xfree(tmp); } } } while (*iochans); return 0; }
static int statserv_sc_main(yaz_sc_t s, int argc, char **argv) { char sep; #ifdef WIN32 /* We need to initialize the thread list */ ThreadList_Initialize(); /* WIN32 */ #endif #ifdef WIN32 sep = '\\'; #else sep = '/'; #endif if ((me = strrchr(argv[0], sep))) me++; /* get the basename */ else me = argv[0]; programname = argv[0]; if (control_block.options_func(argc, argv)) return 1; xml_config_open(); xml_config_bend_start(); #ifdef WIN32 xml_config_add_listeners(); yaz_log(log_server, "Starting server %s", me); if (!pListener && *control_block.default_listen) add_listener(control_block.default_listen, 0); #else /* UNIX */ if (control_block.inetd) inetd_connection(control_block.default_proto); else { static int hand[2]; if (control_block.background) { /* create pipe so that parent waits until child has created PID (or failed) */ if (pipe(hand) < 0) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe"); return 1; } switch (fork()) { case 0: break; case -1: return 1; default: close(hand[1]); while(1) { char dummy[1]; int res = read(hand[0], dummy, 1); if (res < 0 && yaz_errno() != EINTR) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake"); break; } else if (res >= 0) break; } close(hand[0]); _exit(0); } /* child */ close(hand[0]); if (setsid() < 0) return 1; close(0); close(1); close(2); open("/dev/null", O_RDWR); if (dup(0) == -1) return 1; if (dup(0) == -1) return 1; } xml_config_add_listeners(); if (!pListener && *control_block.default_listen) add_listener(control_block.default_listen, 0); if (!pListener) return 1; if (*control_block.pid_fname) { FILE *f = fopen(control_block.pid_fname, "w"); if (!f) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "Couldn't create %s", control_block.pid_fname); exit(0); } fprintf(f, "%ld", (long) getpid()); fclose(f); } if (control_block.background) close(hand[1]); yaz_log(log_server, "Starting server %s pid=%ld", programname, (long) getpid()); #if 0 sigset_t sigs_to_block; sigemptyset(&sigs_to_block); sigaddset(&sigs_to_block, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigs_to_block, 0); /* missing... */ #endif if (control_block.dynamic) signal(SIGCHLD, catchchld); } signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sigterm); if (*control_block.setuid) { struct passwd *pw; if (!(pw = getpwnam(control_block.setuid))) { yaz_log(YLOG_FATAL, "%s: Unknown user", control_block.setuid); return(1); } if (setuid(pw->pw_uid) < 0) { yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid"); exit(1); } } /* UNIX */ #endif if (pListener == NULL) return 1; if (s) yaz_sc_running(s); yaz_log(YLOG_DEBUG, "Entering event loop."); return iochan_event_loop(&pListener); }