From 3e31e7be9c52a59172ed4a48f227e11d503dd6c6 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Thu, 18 Jul 2024 08:25:31 -0400 Subject: [PATCH 01/21] Don't fail in gz_load() if read would block I.e. when [errno] is EAGAIN or EWOULDBLOCK) E.g. if file descriptor is a non-blocking TCP socket, and more data are expected. This might cause a problem if compressed bytes are read but do not generate an uncompressed byte, because the return value will be 0, which usually indicates EOF. --- gzread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gzread.c b/gzread.c index 2d4ca4290..0c4e83ed6 100644 --- a/gzread.c +++ b/gzread.c @@ -24,7 +24,7 @@ local int gz_load(gz_statep state, unsigned char *buf, unsigned len, break; *have += (unsigned)ret; } while (*have < len); - if (ret < 0) { + if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { gz_error(state, Z_ERRNO, zstrerror()); return -1; } From f16f6560e343b74c59cefe1552a5f500781e4170 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Sat, 20 Jul 2024 15:54:44 -0400 Subject: [PATCH 02/21] Handle EWOULDBLOCK/EAGAIN in gz_load() call from gz_read --- gzread.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gzread.c b/gzread.c index 0c4e83ed6..67e5416f0 100644 --- a/gzread.c +++ b/gzread.c @@ -268,6 +268,7 @@ local int gz_skip(gz_statep state, z_off64_t len) { local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { z_size_t got; unsigned n; + int load_errno; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -283,6 +284,7 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { /* get len bytes to buf, or less than len if at the end */ got = 0; do { + load_errno = 0; /* set n to the maximum amount of len that fits in an unsigned int */ n = (unsigned)-1; if (n > len) @@ -316,8 +318,10 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { /* large len -- read directly into user buffer */ else if (state->how == COPY) { /* read directly */ + errno = 0; if (gz_load(state, (unsigned char *)buf, n, &n) == -1) return 0; + load_errno = errno; } /* large len -- decompress directly into user buffer */ @@ -335,7 +339,7 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { buf = (char *)buf + n; got += n; state->x.pos += n; - } while (len); + } while (len && load_errno != EAGAIN && load_errno != EWOULDBLOCK); /* return number of bytes read into user buffer */ return got; From adb2b5b97c4b123e9f691d905d85466e0287dced Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Sat, 20 Jul 2024 18:04:34 -0400 Subject: [PATCH 03/21] More handling of EWOULDBLOCK/EAGAIN results in calls from gz_read --- gzread.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gzread.c b/gzread.c index 67e5416f0..02a5640b0 100644 --- a/gzread.c +++ b/gzread.c @@ -309,8 +309,10 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { buffer */ else if (state->how == LOOK || n < (state->size << 1)) { /* get more output, looking for header if required */ + errno = 0; if (gz_fetch(state) == -1) return 0; + load_errno = errno; continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ @@ -328,8 +330,10 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { else { /* state->how == GZIP */ state->strm.avail_out = n; state->strm.next_out = (unsigned char *)buf; + errno = 0; if (gz_decomp(state) == -1) return 0; + load_errno = errno; n = state->x.have; state->x.have = 0; } From 6e8ddfd960d9d9c2657cc6be3d5f192b72c8cfba Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Sun, 21 Jul 2024 09:09:37 -0400 Subject: [PATCH 04/21] In gz_read(), do not introduce blocked read state after gz_fetch() until data are transferred to buffer argument --- gzread.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gzread.c b/gzread.c index 02a5640b0..98b64c149 100644 --- a/gzread.c +++ b/gzread.c @@ -312,7 +312,7 @@ local z_size_t gz_read(gz_statep state, voidp buf, z_size_t len) { errno = 0; if (gz_fetch(state) == -1) return 0; - load_errno = errno; + if (!state->x.have) { load_errno = errno; } continue; /* no progress yet -- go back to copy above */ /* the copy above assures that we will leave with space in the output buffer, allowing at least one gzungetc() to succeed */ From 3ddf53827af981bde0b78d02202f0e569dc0477f Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 14:17:13 -0400 Subject: [PATCH 05/21] Add non-blocking socket test code; first draft, no formal test yet --- test/gz_nonblock_socket.c | 404 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 test/gz_nonblock_socket.c diff --git a/test/gz_nonblock_socket.c b/test/gz_nonblock_socket.c new file mode 100644 index 000000000..18e131516 --- /dev/null +++ b/test/gz_nonblock_socket.c @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 128 +const struct timeval tvfixed = { 1, 0 }; + +/* Wrapper to make file non-blocking */ + +int +make_fd_nonblocking(int fd) +{ + int existing_flags = fcntl(fd, F_GETFL); + return (-1 == existing_flags) + ? -2 + : fcntl(fd, F_SETFL, existing_flags | O_NONBLOCK); +} + +/* Server code to use gzread.c/zlib.h library: + * - Listen for, and accept, socket connection(s) + * - select(2), and (gz)read data when available + * - Exit when is received + */ + +int +servermain(int argc, char** argv) +{ + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *rp; + int listenfd; + int sfd; + int s; + struct sockaddr_storage peer_addr; + socklen_t peerlen = sizeof peer_addr; + ssize_t nread; + char buf[BUF_SIZE]; + gzFile gzfi = NULL; + + if (argc != 2) { + fprintf(stderr, "Server usage: %s port\n", argv[0]); + return EXIT_FAILURE; + } + + /* Setup for getaddrinfo(3) call */ + + memset(&hints, 0, sizeof(hints)); + //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + //hints.ai_family = AF_INET6; /* IPv6 */ + hints.ai_family = AF_INET; /* IPv4 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; /* Server; nobbut else needed */ + hints.ai_addr = NULL; + hints.ai_next = NULL; + + /* Get address info: NULL => server; argv[1] is the port */ + s = getaddrinfo(NULL, argv[1], &hints, &result); + if (s != 0) { + fprintf(stderr, "Server getaddrinfo failed: %s\n", gai_strerror(s)); + return EXIT_FAILURE; + } + + /* getaddrinfo() returns a list of address structures. + * Try each address structure until we successfully bind(2). + * If socket(2) or bind(2) or listen(2) fails, then we try the next + * address structure after closing the socket if socket(2) succeeded + */ + for (rp = result; rp != NULL; rp = rp->ai_next) { + listenfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (listenfd == -1) { continue; } + + if (!bind(listenfd, rp->ai_addr, rp->ai_addrlen) + && !listen(listenfd, 10) + ) + { + break; /* Success */ + } + close(listenfd); + } + + freeaddrinfo(result); /* No longer needed */ + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Server could not socket/bind/listen\n"); + return EXIT_FAILURE; + } + + sfd=-1; + /* Loop: + * - select(2) on EITHER listening OR connected socket + * - accept a new socket connection if listening socket has data + * - select(2)/gzread data from connected socket, print to stderr + * - Close when socket is closed + * + * zs is the number of consecutive times select(2) returns 0 + */ + for (int zs = 0;; ++zs) + { + int rtn; + int nfd; + fd_set rfds; + struct timeval tv; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + /* Clear FD set */ + FD_ZERO(&rfds); + + if (gzfi) + { + /* Set accepted socket's bit if gzfi is not NULL */ + FD_SET(sfd, &rfds); + nfd = sfd + 1; + } + else + { + /* Set listening socket's bit if gzfi is NULL */ + FD_SET(listenfd, &rfds); + nfd = listenfd + 1; + } + + /* Wait for data from selected file */ + errno = 0; + tv = tvfixed; + rtn = select(nfd, &rfds, NULL, NULL, &tv); + if (rtn) + { + /* Log non-zero return values from select(2) */ + fprintf(stderr, "Server %d=select(nfd,%llx,,,tv)" + "; errno=%d[%s]\n" + , rtn, *((long long*)&rfds) + , errno, strerror(errno) + ); + zs = 0; + } + else + { + /* Count and log consecutive passes with no incoming data */ + const char terms[] = { "|/-\\" }; + fprintf(stderr, "%d%c\r", zs, terms[zs&3]); + continue; + } + + if (rtn < 0) /* select failed */ + { + select(0, NULL, NULL, NULL, &tv); /* finish delay */ + continue; /* Ignore failed select */ + } + + /* to here, select(2) returned 1 */ + + if (gzfi) /* if accepted socket is active, handle I/O */ + { + char* p; + char* pend; + int ipos; + + //not necessary: if (!FD_ISSET(sfd, &rfds)) continue; + + /* Read data */ + errno = 0; + gzclearerr(gzfi); + rtn = gzread(gzfi, buf, sizeof buf); + fprintf(stderr, "Server %d=gzread(%d,...)" + "; errno=%d[%s]\n" + , rtn, sfd, errno, strerror(errno) + ); + + /* Handle EOF (rtn==0) or error (rtn<0) */ + if (rtn < 1) + { + int igzerr; + const char* pgzerr = gzerror(gzfi, &igzerr); + fprintf(stderr, "Server %d=gzerror[%s]\n" + , igzerr, pgzerr ? pgzerr : "" + ); + gzclose(gzfi); + gzfi = NULL; + sfd = -1; + continue; + } + + /* Log data read by gzread */ + fprintf(stderr, "%s", "Server buf=>["); + for (pend = (p=buf) + rtn; p", *p); + } + fprintf(stderr, "%s", "]\n"); + + continue; /* Done with gzread over socket; skip listenfd */ + + } /* if (gzfi) */ + + /* To here, there is no active accepted socket; select(2) result + * was for bound listining socket, indicating a new connection + * request + */ + + //not necessary: if (!FD_ISSET(listenfd, &rfds)) { continue; } + + /* Accept the new connection */ + errno = 0; + sfd = accept(listenfd, (struct sockaddr *)&peer_addr, &peerlen); + if (sfd < 0) + { + fprintf(stderr, "Server %d=accept(listenfd,...)" + "; errno=%d[%s]\n" + , rtn, errno, strerror(errno) + ); + continue; + } + + /* Make the new socket non-blocking */ + errno = 0; + if (0 > make_fd_nonblocking(sfd)) + { + fprintf(stderr, "Server %d=make_fd_nonblocking(%d,...)" + "; errno=%d[%s]\n" + , rtn, sfd, errno, strerror(errno) + ); + close(sfd); + sfd = -1; + continue; + } + + /* Log information about the newly-accepted socket */ + s = getnameinfo((struct sockaddr *)&peer_addr, peerlen + , host, NI_MAXHOST + , service, NI_MAXSERV, NI_NUMERICSERV); + if (s == 0) + { + fprintf(stderr, "Server accepted connection from %s:%s\n", host, service); + } + else + { + fprintf(stderr, "Server getnameinfo failed: %s\n", gai_strerror(s)); + } + + /* Open/allocate the gzFile, and set its buffer size */ + if (!(gzfi = gzdopen(sfd, "r"))) + { + fprintf(stderr, "Server gzdopen(%d, \"r\") failed\n" , sfd); + close(sfd); + sfd = -1; + continue; + } + if (gzbuffer(gzfi, 16)) + { + fprintf(stderr, "%s\n", "Server gzbuffer(gzfi, 16) failed"); + gzclose(gzfi); + gzfi = NULL; + sfd = -1; + continue; + } + } // for (int zs = 0;; ++zs) + + return EXIT_SUCCESS; +} + +int +clientmain(int argc, char** argv) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + size_t len; + ssize_t nread; + char buf[BUF_SIZE]; + gzFile gzfi = NULL; + int final_rtn = EXIT_SUCCESS; + + if (argc < 3) { + fprintf(stderr, "Client usage: %s host port msg...\n", argv[0]); + return EXIT_FAILURE; + } + + /* Obtain address(es) matching host/port */ + + memset(&hints, 0, sizeof(hints)); + //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + //hints.ai_family = AF_INET6; /* Allow IPv4 or IPv6 */ + hints.ai_family = AF_INET; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(argv[1], argv[2], &hints, &result); + if (s != 0) { + fprintf(stderr, "Client getaddrinfo failed: %s\n", gai_strerror(s)); + return EXIT_FAILURE; + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully connect(2). + If socket(2) (or connect(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (sfd == -1) { continue; } /* Ignore failed socket creation*/ + + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) { break; } + /* ^ Exit loop on Successful connection */ + /* v Close socket and continue on Failed connection */ + close(sfd); + } + + freeaddrinfo(result); /* getaddrinfo result is no longer needed */ + + if (rp == NULL) /* No address means all failed */ + { + fprintf(stderr, "Client could not connect\n"); + return EXIT_FAILURE; + } + + if (!(gzfi = gzdopen(sfd, "w"))) + { + fprintf(stderr, "Client gzdopen(%d, \"w\") failed\n" , sfd); + close(sfd); + sfd = -1; + return EXIT_FAILURE; + } + + /* Send remaining command-line arguments as separate writes */ + + for (int j = 3; final_rtn == EXIT_SUCCESS && j < argc; j++) + { + int rtn; + int itmp; + int save_errno; + + len = strlen(argv[j]) + 1; /* +1 for terminating null byte */ + + if (len > BUF_SIZE) + { + fprintf(stderr, "Client Ignoring long message" + " in argument %d\n", j + ); + continue; + } + + if (!strcmp(argv[j],"--delay")) + { + struct timeval tv; + tv = tvfixed; + select(0,NULL,NULL,NULL,&tv); + continue; + } + + errno = 0; + if ((rtn=gzwrite(gzfi, argv[j], len)) != len) + { + save_errno = errno; + fprintf(stderr, "Client partial/failed %d=gzwrite[%s]" + "; %d=errno[%s]" + , rtn, gzerror(gzfi, &itmp) + , save_errno, strerror(save_errno) + ); + final_rtn = EXIT_FAILURE; + continue; + } + + errno = 0; + if ((rtn=gzflush(gzfi, Z_SYNC_FLUSH)) != Z_OK) + { + save_errno = errno; + fprintf(stderr, "Client partial/failed %d=gzbuffer[%s]" + "; %d=errno[%s]" + , rtn, gzerror(gzfi, &itmp) + , save_errno, strerror(save_errno) + ); + final_rtn = EXIT_FAILURE; + continue; + } + } // for (int j = 3; j < argc; j++) + + gzflush(gzfi, Z_FINISH); + //gzclose(gzfi); + + return final_rtn; +} + +int +main(int argc, char** argv) +{ + if (argc < 3) { return servermain(argc, argv); } + return clientmain(argc, argv); +} From 12eca61ee83023de4b443219e11a7da516a477d8 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 15:11:17 -0400 Subject: [PATCH 06/21] Rename test program --- test/{gz_nonblock_socket.c => gznonblk.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{gz_nonblock_socket.c => gznonblk.c} (100%) diff --git a/test/gz_nonblock_socket.c b/test/gznonblk.c similarity index 100% rename from test/gz_nonblock_socket.c rename to test/gznonblk.c From 1cf75570d5262b6c1f8d849ca6907a3b74aa0ddc Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 15:23:23 -0400 Subject: [PATCH 07/21] Build GZ-non-blocking binary --- Makefile.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile.in b/Makefile.in index 5b18f76e4..4f2ccd11c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -77,6 +77,8 @@ shared: examplesh$(EXE) minigzipsh$(EXE) all64: example64$(EXE) minigzip64$(EXE) +staticgznb: gznonblk$(EXE) + check: test test: all teststatic testshared @@ -145,6 +147,9 @@ example.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h minigzip.o: $(SRCDIR)test/minigzip.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/minigzip.c +gznonblk.o: $(SRCDIR)test/gznonblk.c $(SRCDIR)zlib.h zconf.h + $(CC) $(CFLAGS) $(ZINCOUT) -c -o $@ $(SRCDIR)test/gznonblk.c + example64.o: $(SRCDIR)test/example.c $(SRCDIR)zlib.h zconf.h $(CC) $(CFLAGS) $(ZINCOUT) -D_FILE_OFFSET_BITS=64 -c -o $@ $(SRCDIR)test/example.c @@ -287,6 +292,9 @@ example$(EXE): example.o $(STATICLIB) minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS) +gznonblk$(EXE): gznonblk.o $(STATICLIB) + $(CC) $(CFLAGS) -o $@ gznonblk.o $(LDFLAGS) -L. $(TEST_LIBS) + examplesh$(EXE): example.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -L. $(SHAREDLIBV) From d7816057415811403952b74f5057878adb92af5a Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 17:43:49 -0400 Subject: [PATCH 08/21] Add fork of client by server in test/gznonblk.c; not done yet --- test/gznonblk.c | 83 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 18e131516..7e2fd345a 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -272,7 +272,10 @@ int clientmain(int argc, char** argv) { struct addrinfo hints; - struct addrinfo *result, *rp; + struct addrinfo *result; + struct addrinfo *rp; + int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); + char* serverhost; int sfd, s; size_t len; ssize_t nread; @@ -280,11 +283,17 @@ clientmain(int argc, char** argv) gzFile gzfi = NULL; int final_rtn = EXIT_SUCCESS; - if (argc < 3) { - fprintf(stderr, "Client usage: %s host port msg...\n", argv[0]); + if (argc < 3 || (argc == 3 && clientfork)) + { + fprintf(stderr, "Client usage: %s %s" + " serverhost msg...\n" + , argv[0], clientfork ? " --client-fork" : "" + ); return EXIT_FAILURE; } + serverhost = clientfork ? argv[3] : argv[2]; + /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(hints)); @@ -298,7 +307,7 @@ clientmain(int argc, char** argv) hints.ai_addr = NULL; hints.ai_next = NULL; - s = getaddrinfo(argv[1], argv[2], &hints, &result); + s = getaddrinfo(serverhost, argv[1], &hints, &result); if (s != 0) { fprintf(stderr, "Client getaddrinfo failed: %s\n", gai_strerror(s)); return EXIT_FAILURE; @@ -339,23 +348,26 @@ clientmain(int argc, char** argv) /* Send remaining command-line arguments as separate writes */ - for (int j = 3; final_rtn == EXIT_SUCCESS && j < argc; j++) + for (int iarg = clientfork ? 4 : 3 + ; final_rtn == EXIT_SUCCESS && iarg < argc + ; ++iarg + ) { int rtn; int itmp; int save_errno; - len = strlen(argv[j]) + 1; /* +1 for terminating null byte */ + len = strlen(argv[iarg]) + 1; /* +1 for terminating null byte */ if (len > BUF_SIZE) { fprintf(stderr, "Client Ignoring long message" - " in argument %d\n", j + " in argument %d\n", iarg ); continue; } - if (!strcmp(argv[j],"--delay")) + if (!strcmp(argv[iarg],"--delay")) { struct timeval tv; tv = tvfixed; @@ -364,7 +376,7 @@ clientmain(int argc, char** argv) } errno = 0; - if ((rtn=gzwrite(gzfi, argv[j], len)) != len) + if ((rtn=gzwrite(gzfi, argv[iarg], len)) != len) { save_errno = errno; fprintf(stderr, "Client partial/failed %d=gzwrite[%s]" @@ -388,10 +400,10 @@ clientmain(int argc, char** argv) final_rtn = EXIT_FAILURE; continue; } - } // for (int j = 3; j < argc; j++) + } // for (int iarg = ...; ...; ++iarg) gzflush(gzfi, Z_FINISH); - //gzclose(gzfi); + gzclose(gzfi); return final_rtn; } @@ -399,6 +411,51 @@ clientmain(int argc, char** argv) int main(int argc, char** argv) { - if (argc < 3) { return servermain(argc, argv); } - return clientmain(argc, argv); + /* Usage: + * gznonblock portnum[[ --client-fork] 127.0.0.1|serverhost[ message1|--delay[ message2|--delay[...]]]] + * + * where + * portnum = port number where server will be listening + * --client-fork = directive to run server and fork client + * 127...|...host = hostname of server for client to use + * messageN|--delay = client messages to send or delays between them + * + * Examples: + * + * gznonblock 4444 + * - Start server only, listening on port 4444 + * + * gznonblock 4444 srvrhst message1 --delay message2 message3 + * - Start client only, connect to server at port 4444 on srvrhost + * - Client + * - sends "message1" + * - delays + * - sends "message2" and "message3" + * + * gznonblock 4444 --client-fork 127.0.0.1 msg1 --delay -stopserver- + * - Fork client, connect to server at port 4444 on 127.0.0.1 + * - Client + * - delays for server to start (forced when forking client) + * - sends "msg1" + * - delays + * - sends "-stopserver-" + * - which stops server + * - Start server, listening on port 4444 + */ + int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); + if (argc > 2 && !clientfork) { return clientmain(argc, argv); } + if (clientfork) + { + pid_t pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Server %d=fork() of client failed" + "; %d=errno[%s]" + , pid, errno, strerror(errno) + ); + return EXIT_FAILURE; + } + if (pid) { return clientmain(argc, argv); } + } + if (argc == 2 || clientfork) { return servermain(2, argv); } } From 73b57d48a695c0ea438b39e98f2f1ed57ba62e57 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 18:07:20 -0400 Subject: [PATCH 09/21] Remove [int s]; use '-stopserver-' to stop server --- test/gznonblk.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 7e2fd345a..55ff49741 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -38,7 +38,8 @@ servermain(int argc, char** argv) struct addrinfo *rp; int listenfd; int sfd; - int s; + int rtn; + int stopserver = 0; struct sockaddr_storage peer_addr; socklen_t peerlen = sizeof peer_addr; ssize_t nread; @@ -64,9 +65,9 @@ servermain(int argc, char** argv) hints.ai_next = NULL; /* Get address info: NULL => server; argv[1] is the port */ - s = getaddrinfo(NULL, argv[1], &hints, &result); - if (s != 0) { - fprintf(stderr, "Server getaddrinfo failed: %s\n", gai_strerror(s)); + rtn = getaddrinfo(NULL, argv[1], &hints, &result); + if (rtn != 0) { + fprintf(stderr, "Server getaddrinfo failed: %s\n", gai_strerror(rtn)); return EXIT_FAILURE; } @@ -104,9 +105,8 @@ servermain(int argc, char** argv) * * zs is the number of consecutive times select(2) returns 0 */ - for (int zs = 0;; ++zs) + for (int zs = 0; !stopserver; ++zs) { - int rtn; int nfd; fd_set rfds; struct timeval tv; @@ -198,6 +198,9 @@ servermain(int argc, char** argv) } fprintf(stderr, "%s", "]\n"); + stopserver = (rtn == 12 || (rtn == 13 && !buf[12])) + && !strncmp("-stopserver-", buf, 12); + continue; /* Done with gzread over socket; skip listenfd */ } /* if (gzfi) */ @@ -235,16 +238,16 @@ servermain(int argc, char** argv) } /* Log information about the newly-accepted socket */ - s = getnameinfo((struct sockaddr *)&peer_addr, peerlen - , host, NI_MAXHOST - , service, NI_MAXSERV, NI_NUMERICSERV); - if (s == 0) + rtn = getnameinfo((struct sockaddr *)&peer_addr, peerlen + , host, NI_MAXHOST + , service, NI_MAXSERV, NI_NUMERICSERV); + if (rtn == 0) { fprintf(stderr, "Server accepted connection from %s:%s\n", host, service); } else { - fprintf(stderr, "Server getnameinfo failed: %s\n", gai_strerror(s)); + fprintf(stderr, "Server getnameinfo failed: %s\n", gai_strerror(rtn)); } /* Open/allocate the gzFile, and set its buffer size */ @@ -263,7 +266,7 @@ servermain(int argc, char** argv) sfd = -1; continue; } - } // for (int zs = 0;; ++zs) + } // for (int zs = 0; !stopserver; ++zs) return EXIT_SUCCESS; } @@ -276,7 +279,8 @@ clientmain(int argc, char** argv) struct addrinfo *rp; int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); char* serverhost; - int sfd, s; + int sfd; + int rtn; size_t len; ssize_t nread; char buf[BUF_SIZE]; @@ -307,9 +311,9 @@ clientmain(int argc, char** argv) hints.ai_addr = NULL; hints.ai_next = NULL; - s = getaddrinfo(serverhost, argv[1], &hints, &result); - if (s != 0) { - fprintf(stderr, "Client getaddrinfo failed: %s\n", gai_strerror(s)); + rtn = getaddrinfo(serverhost, argv[1], &hints, &result); + if (!rtn) { + fprintf(stderr, "Client getaddrinfo failed: %s\n", gai_strerror(rtn)); return EXIT_FAILURE; } @@ -353,7 +357,6 @@ clientmain(int argc, char** argv) ; ++iarg ) { - int rtn; int itmp; int save_errno; @@ -439,7 +442,7 @@ main(int argc, char** argv) * - sends "msg1" * - delays * - sends "-stopserver-" - * - which stops server + * - which will stop server later * - Start server, listening on port 4444 */ int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); From a9f3755bcdea92e0570b0a53b16004425f6f6a2f Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 18:42:11 -0400 Subject: [PATCH 10/21] Fix post-fork(2) bug; clean up comments --- test/gznonblk.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 55ff49741..e23cbe4db 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -57,7 +57,7 @@ servermain(int argc, char** argv) //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ //hints.ai_family = AF_INET6; /* IPv6 */ hints.ai_family = AF_INET; /* IPv4 */ - hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; /* Server; nobbut else needed */ @@ -66,9 +66,12 @@ servermain(int argc, char** argv) /* Get address info: NULL => server; argv[1] is the port */ rtn = getaddrinfo(NULL, argv[1], &hints, &result); - if (rtn != 0) { - fprintf(stderr, "Server getaddrinfo failed: %s\n", gai_strerror(rtn)); - return EXIT_FAILURE; + if (rtn) { + fprintf(stderr, "Client %d=getaddrinfo(\"%s\",\"%s\",...)" + " failed: %s\n" + , rtn, "", argv[1] + , strerror(errno) + ); } /* getaddrinfo() returns a list of address structures. @@ -301,19 +304,24 @@ clientmain(int argc, char** argv) /* Obtain address(es) matching host/port */ memset(&hints, 0, sizeof(hints)); - //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - //hints.ai_family = AF_INET6; /* Allow IPv4 or IPv6 */ - hints.ai_family = AF_INET; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + //hints.ai_family = AF_INET6; /* Allow IPv6 only */ + hints.ai_family = AF_INET; /* Allow IPv4 only */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ hints.ai_flags = 0; - hints.ai_protocol = 0; /* Any protocol */ + hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; + errno = 0; rtn = getaddrinfo(serverhost, argv[1], &hints, &result); - if (!rtn) { - fprintf(stderr, "Client getaddrinfo failed: %s\n", gai_strerror(rtn)); + if (rtn) { + fprintf(stderr, "Client %d=getaddrinfo(\"%s\",\"%s\",...)" + " failed: %s\n" + , rtn, serverhost, argv[1] + , strerror(errno) + ); return EXIT_FAILURE; } @@ -447,18 +455,23 @@ main(int argc, char** argv) */ int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); if (argc > 2 && !clientfork) { return clientmain(argc, argv); } + errno = 0; if (clientfork) { pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "Server %d=fork() of client failed" - "; %d=errno[%s]" + "; %d=errno[%s]\n" , pid, errno, strerror(errno) ); return EXIT_FAILURE; } - if (pid) { return clientmain(argc, argv); } + if (!pid) { return clientmain(argc, argv); } + fprintf(stderr, "Server %d=fork()=PID of client succeeded" + "; %d=errno[%s]\n" + , pid, errno, strerror(errno) + ); } if (argc == 2 || clientfork) { return servermain(2, argv); } } From ea6e6776bfa3842df045d0d73e24d5560ae2eecc Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 20:52:59 -0400 Subject: [PATCH 11/21] test/gznonblk.c now working, server spawns client and reports exit status of client and server suitable for automated testing (make test...) --- test/gznonblk.c | 169 +++++++++++++++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 60 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index e23cbe4db..3f25e08cb 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -67,11 +68,12 @@ servermain(int argc, char** argv) /* Get address info: NULL => server; argv[1] is the port */ rtn = getaddrinfo(NULL, argv[1], &hints, &result); if (rtn) { - fprintf(stderr, "Client %d=getaddrinfo(\"%s\",\"%s\",...)" + fprintf(stderr, "Server %d=getaddrinfo(\"%s\",\"%s\",...)" " failed: %s\n" , rtn, "", argv[1] - , strerror(errno) + , gai_strerror(rtn) ); + return EXIT_FAILURE; } /* getaddrinfo() returns a list of address structures. @@ -320,7 +322,7 @@ clientmain(int argc, char** argv) fprintf(stderr, "Client %d=getaddrinfo(\"%s\",\"%s\",...)" " failed: %s\n" , rtn, serverhost, argv[1] - , strerror(errno) + , gai_strerror(rtn) ); return EXIT_FAILURE; } @@ -366,7 +368,7 @@ clientmain(int argc, char** argv) ) { int itmp; - int save_errno; + int save_err; len = strlen(argv[iarg]) + 1; /* +1 for terminating null byte */ @@ -389,11 +391,11 @@ clientmain(int argc, char** argv) errno = 0; if ((rtn=gzwrite(gzfi, argv[iarg], len)) != len) { - save_errno = errno; + save_err = errno; fprintf(stderr, "Client partial/failed %d=gzwrite[%s]" "; %d=errno[%s]" , rtn, gzerror(gzfi, &itmp) - , save_errno, strerror(save_errno) + , save_err, strerror(save_err) ); final_rtn = EXIT_FAILURE; continue; @@ -402,11 +404,11 @@ clientmain(int argc, char** argv) errno = 0; if ((rtn=gzflush(gzfi, Z_SYNC_FLUSH)) != Z_OK) { - save_errno = errno; + save_err = errno; fprintf(stderr, "Client partial/failed %d=gzbuffer[%s]" "; %d=errno[%s]" , rtn, gzerror(gzfi, &itmp) - , save_errno, strerror(save_errno) + , save_err, strerror(save_err) ); final_rtn = EXIT_FAILURE; continue; @@ -422,56 +424,103 @@ clientmain(int argc, char** argv) int main(int argc, char** argv) { - /* Usage: - * gznonblock portnum[[ --client-fork] 127.0.0.1|serverhost[ message1|--delay[ message2|--delay[...]]]] - * - * where - * portnum = port number where server will be listening - * --client-fork = directive to run server and fork client - * 127...|...host = hostname of server for client to use - * messageN|--delay = client messages to send or delays between them - * - * Examples: - * - * gznonblock 4444 - * - Start server only, listening on port 4444 - * - * gznonblock 4444 srvrhst message1 --delay message2 message3 - * - Start client only, connect to server at port 4444 on srvrhost - * - Client - * - sends "message1" - * - delays - * - sends "message2" and "message3" - * - * gznonblock 4444 --client-fork 127.0.0.1 msg1 --delay -stopserver- - * - Fork client, connect to server at port 4444 on 127.0.0.1 - * - Client - * - delays for server to start (forced when forking client) - * - sends "msg1" - * - delays - * - sends "-stopserver-" - * - which will stop server later - * - Start server, listening on port 4444 - */ - int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); - if (argc > 2 && !clientfork) { return clientmain(argc, argv); } - errno = 0; - if (clientfork) - { - pid_t pid = fork(); - if (pid < 0) - { - fprintf(stderr, "Server %d=fork() of client failed" - "; %d=errno[%s]\n" - , pid, errno, strerror(errno) - ); - return EXIT_FAILURE; - } - if (!pid) { return clientmain(argc, argv); } - fprintf(stderr, "Server %d=fork()=PID of client succeeded" - "; %d=errno[%s]\n" - , pid, errno, strerror(errno) - ); - } - if (argc == 2 || clientfork) { return servermain(2, argv); } + /* Usage: + * gznonblock portnum[[ --client-fork] 127.0.0.1|serverhost[ message1|--delay[ message2|--delay[...]]]] + * + * where + * portnum = port number where server will be listening + * --client-fork = directive to run server and fork client + * 127...|...host = hostname of server for client to use + * messageN|--delay = client messages to send or delays between them + * + * Examples: + * + * gznonblock 4444 + * - Start server only, listening on port 4444 + * + * gznonblock 4444 srvrhst message1 --delay message2 message3 + * - Start client only, connect to server at port 4444 on srvrhost + * - Client + * - sends "message1" + * - delays + * - sends "message2" and "message3" + * + * gznonblock 4444 --client-fork 127.0.0.1 msg1 --delay -stopserver- + * - Fork client, connect to server at port 4444 on 127.0.0.1 + * - Client + * - delays for server to start (forced when forking client) + * - sends "msg1" + * - delays + * - sends "-stopserver-" + * - which will stop server later + * - Start server, listening on port 4444 + */ + int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); + + if (argc > 2 && !clientfork) { return clientmain(argc, argv); } + + errno = 0; + if (clientfork) + { + int rtn; + pid_t pidwaited; + int wstat; + int wopts = WNOHANG; + struct timeval tv = {3, 0}; + int badchild = 0; + + pid_t pidforked = fork(); + + int save_err = errno; + + if (pidforked < 0) + { + fprintf(stderr, "Server %d=fork() of client failed" + "; %d=errno[%s]\n" + , pidforked, save_err, strerror(save_err) + ); + return EXIT_FAILURE; + } + + /* Child runs client and exits */ + if (!pidforked) { return clientmain(argc, argv); } + + /* To here, this is the server */ + fprintf(stderr, "Server %d=fork()=PID of client succeeded" + "; %d=errno[%s]\n" + , pidforked, save_err, strerror(save_err) + ); + + rtn = servermain(2, argv); /* Run server */ + select(0, NULL, NULL, NULL, &tv); /* Wait for child */ + errno = 0; + pidwaited = waitpid(-1, &wstat, wopts); /* Get child status */ + + if (pidwaited < 1) /* Non-positive return is a failure */ + { + ++badchild; + fprintf(stderr, "Server child %d=waitpid(-1,...) failed" + "; %d=errno[%s]\n" + , pidwaited, errno, strerror(errno) + ); + return EXIT_FAILURE; + } + + if (pidwaited != pidforked) /* Wait PID must match fork PID */ + { + ++badchild; + fprintf(stderr, "Server child %d=waitpid(-1,...) not equal" + " to forked pid (%d)\n" + , pidwaited, pidforked + ); + return EXIT_FAILURE; + } + + /* Combine server status and child/client status */ + return rtn | (WIFEXITED(wstat) ? WEXITSTATUS(wstat) : 1); + } + + /* Run server only */ + if (argc == 2) { return servermain(2, argv); } + return -1; } From f6a01bb0118d99d3ee5ccf75b27e9b27f8789c8d Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Tue, 23 Jul 2024 21:01:01 -0400 Subject: [PATCH 12/21] Update Makefile.in to run test of gznonblk --- Makefile.in | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 4f2ccd11c..f55cb2cf7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -81,7 +81,7 @@ staticgznb: gznonblk$(EXE) check: test -test: all teststatic testshared +test: all teststatic testshared testgznonblk teststatic: static @TMPST=tmpst_$$; \ @@ -92,6 +92,13 @@ teststatic: static fi @rm -f tmpst_$$ +testgznonblk: staticgznb + @if ./gznonblk 44444 --client-fork 127.0.0.1 first second --delay third --delay -stopserver- ; then \ + echo ' *** gznonblk test OK ***'; \ + else \ + echo ' *** gznonblk test FAILED ***'; false; \ + fi + testshared: shared @LD_LIBRARY_PATH=`pwd`:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ LD_LIBRARYN32_PATH=`pwd`:$(LD_LIBRARYN32_PATH) ; export LD_LIBRARYN32_PATH; \ From 880c2611976e297aaa0f5ad16ba34ea5ea0b7769 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 06:54:06 -0400 Subject: [PATCH 13/21] Add /gznonblk to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e86742897..d50a24a1b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ /minigzip /minigzip64 /minigzipsh +/gznonblk /zlib.pc /configure.log /build From 6366659dbbce9db30825871e13fab67995ddf106 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 07:00:36 -0400 Subject: [PATCH 14/21] Add dependencies for gznonblk* to Makefile.in --- Makefile.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index f55cb2cf7..f6e94a1be 100644 --- a/Makefile.in +++ b/Makefile.in @@ -300,7 +300,7 @@ minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS) gznonblk$(EXE): gznonblk.o $(STATICLIB) - $(CC) $(CFLAGS) -o $@ gznonblk.o $(LDFLAGS) -L. $(TEST_LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ gznonblk.o $(TEST_LIBS) examplesh$(EXE): example.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -L. $(SHAREDLIBV) @@ -385,6 +385,7 @@ clean: minizip-clean rm -f *.o *.lo *~ \ example$(EXE) minigzip$(EXE) examplesh$(EXE) minigzipsh$(EXE) \ example64$(EXE) minigzip64$(EXE) \ + gznonblk$(EXE) \ infcover \ libz.* foo.gz so_locations \ _match.s maketree contrib/infback9/*.o @@ -407,6 +408,7 @@ tags: adler32.o zutil.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h gzclose.o gzlib.o gzread.o gzwrite.o: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h compress.o example.o minigzip.o uncompr.o: $(SRCDIR)zlib.h zconf.h +gznonblk.o: $(SRCDIR)zlib.h zconf.h crc32.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h deflate.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h infback.o inflate.o: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h @@ -417,6 +419,7 @@ trees.o: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)tr adler32.lo zutil.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h gzclose.lo gzlib.lo gzread.lo gzwrite.lo: $(SRCDIR)zlib.h zconf.h $(SRCDIR)gzguts.h compress.lo example.lo minigzip.lo uncompr.lo: $(SRCDIR)zlib.h zconf.h +gznonblk.lo: $(SRCDIR)zlib.h zconf.h crc32.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)crc32.h deflate.lo: $(SRCDIR)deflate.h $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h infback.lo inflate.lo: $(SRCDIR)zutil.h $(SRCDIR)zlib.h zconf.h $(SRCDIR)inftrees.h $(SRCDIR)inflate.h $(SRCDIR)inffast.h $(SRCDIR)inffixed.h From 5c75fcbd94286b7fe0cd8a276080c815ddb67938 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 07:11:21 -0400 Subject: [PATCH 15/21] Move testgznonblk dependency to the right place in Makefile.in --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index f6e94a1be..f8d6da006 100644 --- a/Makefile.in +++ b/Makefile.in @@ -81,9 +81,9 @@ staticgznb: gznonblk$(EXE) check: test -test: all teststatic testshared testgznonblk +test: all teststatic testshared -teststatic: static +teststatic: static testgznonblk @TMPST=tmpst_$$; \ if echo hello world | ${QEMU_RUN} ./minigzip | ${QEMU_RUN} ./minigzip -d && ${QEMU_RUN} ./example $$TMPST ; then \ echo ' *** zlib test OK ***'; \ From 5ab0725821a53e0010e9670e30fdac0c8ae84d2a Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 08:14:13 -0400 Subject: [PATCH 16/21] test/gznonblk.c: add more comments, rearrange logging --- test/gznonblk.c | 163 +++++++++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 63 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 3f25e08cb..66a2f0cb9 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -27,25 +27,26 @@ make_fd_nonblocking(int fd) /* Server code to use gzread.c/zlib.h library: * - Listen for, and accept, socket connection(s) + * - Make connection non-blocking * - select(2), and (gz)read data when available - * - Exit when is received + * - Exit when "-stopserver-" is received */ int servermain(int argc, char** argv) { + struct addrinfo *rp; /* getaddrinfo(3) items */ struct addrinfo hints; struct addrinfo *result; - struct addrinfo *rp; - int listenfd; - int sfd; - int rtn; - int stopserver = 0; struct sockaddr_storage peer_addr; socklen_t peerlen = sizeof peer_addr; - ssize_t nread; - char buf[BUF_SIZE]; - gzFile gzfi = NULL; + + int rtn; /* Return value from many routines */ + int sfd; /* Accepted socket file descriptor, for sending data */ + int listenfd; /* Bound socket file descriptor, for listening */ + int stopserver = 0; /* Flag for when to exit server code */ + char buf[BUF_SIZE]; /* Read (gzread) buffer */ + gzFile gzfi = NULL; /* gzread "file" information */ if (argc != 2) { fprintf(stderr, "Server usage: %s port\n", argv[0]); @@ -55,13 +56,13 @@ servermain(int argc, char** argv) /* Setup for getaddrinfo(3) call */ memset(&hints, 0, sizeof(hints)); - //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - //hints.ai_family = AF_INET6; /* IPv6 */ - hints.ai_family = AF_INET; /* IPv4 */ - hints.ai_socktype = SOCK_STREAM; /* Stream socket */ - hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ - hints.ai_protocol = 0; /* Any protocol */ - hints.ai_canonname = NULL; /* Server; nobbut else needed */ + //hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + //hints.ai_family = AF_INET6; /* IPv6 */ + hints.ai_family = AF_INET; /* IPv4 */ + hints.ai_socktype = SOCK_STREAM; /* Stream socket */ + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; /* Server; nobbut else needed */ hints.ai_addr = NULL; hints.ai_next = NULL; @@ -70,8 +71,7 @@ servermain(int argc, char** argv) if (rtn) { fprintf(stderr, "Server %d=getaddrinfo(\"%s\",\"%s\",...)" " failed: %s\n" - , rtn, "", argv[1] - , gai_strerror(rtn) + , rtn, "", argv[1], gai_strerror(rtn) ); return EXIT_FAILURE; } @@ -203,16 +203,16 @@ servermain(int argc, char** argv) } fprintf(stderr, "%s", "]\n"); + /* If "-stopserver-" was received, then set flag */ stopserver = (rtn == 12 || (rtn == 13 && !buf[12])) && !strncmp("-stopserver-", buf, 12); continue; /* Done with gzread over socket; skip listenfd */ - } /* if (gzfi) */ /* To here, there is no active accepted socket; select(2) result - * was for bound listining socket, indicating a new connection - * request + * was for bound listening socket (listenfd), indicating a new + * connection request */ //not necessary: if (!FD_ISSET(listenfd, &rfds)) { continue; } @@ -248,14 +248,18 @@ servermain(int argc, char** argv) , service, NI_MAXSERV, NI_NUMERICSERV); if (rtn == 0) { - fprintf(stderr, "Server accepted connection from %s:%s\n", host, service); + fprintf(stderr, "Server accepted connection from %s:%s\n" + , host, service + ); } else { - fprintf(stderr, "Server getnameinfo failed: %s\n", gai_strerror(rtn)); + fprintf(stderr, "Server getnameinfo failed: %s\n" + , gai_strerror(rtn) + ); } - /* Open/allocate the gzFile, and set its buffer size */ + /* Allocate/open the gzFile pointer, and set its buffer size */ if (!(gzfi = gzdopen(sfd, "r"))) { fprintf(stderr, "Server gzdopen(%d, \"r\") failed\n" , sfd); @@ -276,21 +280,27 @@ servermain(int argc, char** argv) return EXIT_SUCCESS; } +/* Client code to use gzwrite.c/zlib.h library: + * - Open socket connection to server above + * - Make connection non-blocking + * - gzwrite command-line arguments (argv) + * - Exit after last argument has been "gzwritten" + */ + int clientmain(int argc, char** argv) { + struct addrinfo *rp; /* getaddrinfo(3) items */ struct addrinfo hints; struct addrinfo *result; - struct addrinfo *rp; + + int rtn; /* Return value from many routines */ + int sfd; /* Socket file descriptor */ + size_t len; /* Length of data to send */ + char* serverhost; /* Name of server (argv[2] or argv[3] */ + gzFile gzfi = NULL; /* gzread "file" information */ + int final_rtn = EXIT_SUCCESS; /* Exit code; assume success */ int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); - char* serverhost; - int sfd; - int rtn; - size_t len; - ssize_t nread; - char buf[BUF_SIZE]; - gzFile gzfi = NULL; - int final_rtn = EXIT_SUCCESS; if (argc < 3 || (argc == 3 && clientfork)) { @@ -301,6 +311,14 @@ clientmain(int argc, char** argv) return EXIT_FAILURE; } + /* Extract server hostname apropo the command line: + * + * gznonblk portnumber serverhost ... + * + * OR + * + * gznonblk portnumber --client-fork serverhost ... + */ serverhost = clientfork ? argv[3] : argv[2]; /* Obtain address(es) matching host/port */ @@ -316,13 +334,13 @@ clientmain(int argc, char** argv) hints.ai_addr = NULL; hints.ai_next = NULL; + /* Get server address info: argv[1] is the port */ errno = 0; rtn = getaddrinfo(serverhost, argv[1], &hints, &result); if (rtn) { fprintf(stderr, "Client %d=getaddrinfo(\"%s\",\"%s\",...)" " failed: %s\n" - , rtn, serverhost, argv[1] - , gai_strerror(rtn) + , rtn, serverhost, argv[1], gai_strerror(rtn) ); return EXIT_FAILURE; } @@ -352,6 +370,7 @@ clientmain(int argc, char** argv) return EXIT_FAILURE; } + /* Allocate/open the gzFile pointer */ if (!(gzfi = gzdopen(sfd, "w"))) { fprintf(stderr, "Client gzdopen(%d, \"w\") failed\n" , sfd); @@ -360,34 +379,27 @@ clientmain(int argc, char** argv) return EXIT_FAILURE; } - /* Send remaining command-line arguments as separate writes */ + /* Send remaining command-line arguments as separate gzwrites */ for (int iarg = clientfork ? 4 : 3 ; final_rtn == EXIT_SUCCESS && iarg < argc ; ++iarg ) { - int itmp; - int save_err; + int itmp; /* Unused value from gzerror */ + int save_err; /* cache for errno */ - len = strlen(argv[iarg]) + 1; /* +1 for terminating null byte */ - - if (len > BUF_SIZE) - { - fprintf(stderr, "Client Ignoring long message" - " in argument %d\n", iarg - ); - continue; - } - - if (!strcmp(argv[iarg],"--delay")) + if (!strcmp(argv[iarg],"--delay")) /* argument is "--delay" */ { struct timeval tv; tv = tvfixed; - select(0,NULL,NULL,NULL,&tv); + select(0,NULL,NULL,NULL,&tv); /* Delay, then get next arg */ continue; } + len = strlen(argv[iarg]) + 1; /* +1 for terminating null byte */ + + /* Write argument using gzwrite; on error log and exit */ errno = 0; if ((rtn=gzwrite(gzfi, argv[iarg], len)) != len) { @@ -401,6 +413,7 @@ clientmain(int argc, char** argv) continue; } + /* Flush data to socket */ errno = 0; if ((rtn=gzflush(gzfi, Z_SYNC_FLUSH)) != Z_OK) { @@ -413,8 +426,11 @@ clientmain(int argc, char** argv) final_rtn = EXIT_FAILURE; continue; } - } // for (int iarg = ...; ...; ++iarg) + /* End of argument loop */ + } // for (...; final_return == EXIT_SUCCESS ...; ++iarg) + + /* Execute final flush, close socket, return status */ gzflush(gzfi, Z_FINISH); gzclose(gzfi); @@ -455,24 +471,39 @@ main(int argc, char** argv) * - which will stop server later * - Start server, listening on port 4444 */ + + /* Check if "--client-fork" is at argument offset 2 */ int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); + /* If command line is: + * + * gznonblk portnum serverhost ... + * + * then run client only + */ if (argc > 2 && !clientfork) { return clientmain(argc, argv); } + /* If command line is: + * + * gznonblk portnum --clienthost serverhost ... + * + * then fork client, run server, wait for client + */ errno = 0; if (clientfork) { - int rtn; - pid_t pidwaited; - int wstat; - int wopts = WNOHANG; - struct timeval tv = {3, 0}; - int badchild = 0; + int rtn; /* Return value from many routines */ + int wstat; /* Client return status from waitpid(2) */ + pid_t pidwaited; /* Return value from waitpid(2) */ + int badchild = 0; /* Non-zero flag for any client failure */ + int wopts = WNOHANG; /* Options for waitpid(2) */ + struct timeval tv = {3, 0}; /* Delay 3s for client to exit */ - pid_t pidforked = fork(); + pid_t pidforked = fork(); /* Fork the client */ - int save_err = errno; + int save_err = errno; /* Save the fork(2) result */ + /* On fork(2) error, log and exit */ if (pidforked < 0) { fprintf(stderr, "Server %d=fork() of client failed" @@ -482,7 +513,7 @@ main(int argc, char** argv) return EXIT_FAILURE; } - /* Child runs client and exits */ + /* If this is now forked child, then run client and exit */ if (!pidforked) { return clientmain(argc, argv); } /* To here, this is the server */ @@ -492,9 +523,9 @@ main(int argc, char** argv) ); rtn = servermain(2, argv); /* Run server */ - select(0, NULL, NULL, NULL, &tv); /* Wait for child */ + select(0, NULL, NULL, NULL, &tv); /* Delay for client to exit */ errno = 0; - pidwaited = waitpid(-1, &wstat, wopts); /* Get child status */ + pidwaited = waitpid(-1, &wstat, wopts); /* Get client status */ if (pidwaited < 1) /* Non-positive return is a failure */ { @@ -520,7 +551,13 @@ main(int argc, char** argv) return rtn | (WIFEXITED(wstat) ? WEXITSTATUS(wstat) : 1); } - /* Run server only */ + /* If command line is: + * + * gznonblk portnum + * + * then run server only + */ if (argc == 2) { return servermain(2, argv); } + return -1; } From 511989db7b5a8eb0fa5dd56eb3b32b7d144be8be Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 11:34:08 -0400 Subject: [PATCH 17/21] Fix typo --- test/gznonblk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 66a2f0cb9..b7d0222da 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -454,7 +454,7 @@ main(int argc, char** argv) * gznonblock 4444 * - Start server only, listening on port 4444 * - * gznonblock 4444 srvrhst message1 --delay message2 message3 + * gznonblock 4444 srvrhost message1 --delay message2 message3 * - Start client only, connect to server at port 4444 on srvrhost * - Client * - sends "message1" From 970385d8565822d8402034ad6afe15429447cb75 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 12:05:47 -0400 Subject: [PATCH 18/21] Add usage options (--help[-long]) to test/gznonblk.c --- test/gznonblk.c | 87 +++++++++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index b7d0222da..4e4a4145d 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -437,44 +437,67 @@ clientmain(int argc, char** argv) return final_rtn; } +int Usage(int argc, char** argv) +{ + const char* arrUsage[] = + { "Usage:" + , " gznonblk pn[[ --client-fork] srvrhost[ msg1|--delay[ msg2...]]]" + , "" + , "where" + , " pn = port number where server will be listening" + , " --client-fork = directive to run server and fork client" + , " srvrhost = hostname of server for client to use" + , " msgN|--delay = client messages to send or delays between them" + , NULL + , "" + , "Examples:" + , "" + , " gznonblk 4444" + , " - Start server only, listening on port 4444" + , "" + , " gznonblk 4444 srvrhost message1 --delay message2 message3" + , " - Start client only, connect to server at port 4444 on srvrhost" + , " - Client" + , " - sends \"message1\"" + , " - delays" + , " - sends \"message2\" and \"message3\"" + , "" + , " gznonblk 4444 --client-fork 127.0.0.1 msg1 --delay -stopserver-" + , " - Fork client, connect to server at port 4444 on 127.0.0.1" + , " - Client" + , " - delays for server to start (forced when forking client)" + , " - sends \"msg1\"" + , " - delays" + , " - sends \"-stopserver-\"" + , " - which will stop server later" + , " - Start server, listening on port 4444" + , NULL + }; + int helplong = 0; + + while (--argc) + { + if (!strcmp(argv[argc], "--help")) { break; } + if (!strcmp(argv[argc], "--help-long")) { ++helplong; break; } + } + if (!argc) { return 0; } + + for (char** p=(char**)arrUsage; helplong || *p; ++p) + { + if (!*p) { --helplong; continue; } + fprintf(stdout, "%s\n", *p); + } + return 1; +} + int main(int argc, char** argv) { - /* Usage: - * gznonblock portnum[[ --client-fork] 127.0.0.1|serverhost[ message1|--delay[ message2|--delay[...]]]] - * - * where - * portnum = port number where server will be listening - * --client-fork = directive to run server and fork client - * 127...|...host = hostname of server for client to use - * messageN|--delay = client messages to send or delays between them - * - * Examples: - * - * gznonblock 4444 - * - Start server only, listening on port 4444 - * - * gznonblock 4444 srvrhost message1 --delay message2 message3 - * - Start client only, connect to server at port 4444 on srvrhost - * - Client - * - sends "message1" - * - delays - * - sends "message2" and "message3" - * - * gznonblock 4444 --client-fork 127.0.0.1 msg1 --delay -stopserver- - * - Fork client, connect to server at port 4444 on 127.0.0.1 - * - Client - * - delays for server to start (forced when forking client) - * - sends "msg1" - * - delays - * - sends "-stopserver-" - * - which will stop server later - * - Start server, listening on port 4444 - */ - /* Check if "--client-fork" is at argument offset 2 */ int clientfork = argc > 2 && !strcmp(argv[2], "--client-fork"); + if (Usage(argc, argv)) { return EXIT_SUCCESS; } + /* If command line is: * * gznonblk portnum serverhost ... From 903c3abd09f899e920c6f198abf24fe82d3b8c1c Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 12:29:18 -0400 Subject: [PATCH 19/21] Clean up test/gznonblk.c Usage --- test/gznonblk.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/gznonblk.c b/test/gznonblk.c index 4e4a4145d..3817ab2bb 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -49,7 +49,7 @@ servermain(int argc, char** argv) gzFile gzfi = NULL; /* gzread "file" information */ if (argc != 2) { - fprintf(stderr, "Server usage: %s port\n", argv[0]); + fprintf(stderr, "Server usage: %s port|service\n", argv[0]); return EXIT_FAILURE; } @@ -304,7 +304,7 @@ clientmain(int argc, char** argv) if (argc < 3 || (argc == 3 && clientfork)) { - fprintf(stderr, "Client usage: %s %s" + fprintf(stderr, "Client usage: %s port|service%s" " serverhost msg...\n" , argv[0], clientfork ? " --client-fork" : "" ); @@ -442,9 +442,10 @@ int Usage(int argc, char** argv) const char* arrUsage[] = { "Usage:" , " gznonblk pn[[ --client-fork] srvrhost[ msg1|--delay[ msg2...]]]" + , " gznonblk --help[-long]" , "" , "where" - , " pn = port number where server will be listening" + , " pn = port# or service where server will be listening" , " --client-fork = directive to run server and fork client" , " srvrhost = hostname of server for client to use" , " msgN|--delay = client messages to send or delays between them" From c6152bbecb59ead3fa4a32faf819e474e8e6a1e0 Mon Sep 17 00:00:00 2001 From: "Brian T. Carcich" Date: Wed, 24 Jul 2024 12:51:09 -0400 Subject: [PATCH 20/21] test/gznonblk.c: add header comments --- test/gznonblk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/gznonblk.c b/test/gznonblk.c index 3817ab2bb..62b8bd192 100644 --- a/test/gznonblk.c +++ b/test/gznonblk.c @@ -1,3 +1,8 @@ +/* gznonblk.c -- test non-blocking reads w/zlib compression library + * Copyright (C) 2024 Brian T. Carcich + * For conditions of distribution and use, see copyright notice in zlib.h + */ + #include #include #include From df4df935d6b0eebfc3174ff73c453f36d3831021 Mon Sep 17 00:00:00 2001 From: Brian Carcich Date: Thu, 25 Jul 2024 10:16:15 -0400 Subject: [PATCH 21/21] Update ChangeLog --- ChangeLog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1f83ab05c..aedf4cf7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,8 @@ ChangeLog file for zlib -Changes in 1.3.1.1 (xx Jan 2024) -- +Changes in 1.3.1.1 (xx Jul 2024) +- Make non-blocking I/O work in gzread.c (Issue #992) Changes in 1.3.1 (22 Jan 2024) - Reject overflows of zip header fields in minizip