I am learning APUE.2e (Advanced Programming in the UNIX® Environment, Second Edition) these days and have got to the chapter 16 Network IPC: sockets. When I run the program ruptime
(for the client end) and ruptimed
(for the server end), something weird happened. The client program shows a client command that communicates with a server to obtain the output from a
system's uptime
command, and the server program ruptimed
(note the 'd') receives the command, exec's the uptime
command and return the output of uptime
to the client. The two programs compiled OK, and I add the ruptime 4000/tcp
to the file /etc/service
which means the service ruptime
binds to the port 4000/tcp
. I ran the two programs on the same machine (Ubuntu 11.04) and the ruptimed
first (of course):
$ ./ruptimed
$ ./ruptime 127.0.0.1
21:35:48 up 13:06, 3 users, load average: 0.56, 0.85, 0.94
The output is what I have expected. However, when I ran:
$ ./ruptimed
$ ./ruptime 192.168.1.221
# the terminal blocks here
The IP address 192.168.1.221 is the machine's actual IP in the intranet. What's wrong here? I searched the Internet, and found out it seems to need a shell script in the /etc/init.d/
. Then I add a file named ruptime
in the /etc/init.d/
directory. Here it is:
#! /bin/sh
### BEGIN INIT INFO
# Provides: ruptimed
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: ruptime
### END INIT INFO
start()
{
echo "start ruptime"
/home/tlh1987/apue-practice/tlh-practice/ruptimed
exit 0;
}
stop()
{
killall ruptimed
echo "stop ruptime"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "usage: $0 start|stop|restart"
exit 0;
esac
However, it didn't work and I don't know why. I also set the ufw enabled and allowed ruptime like this:
sudo ufw allow ruptime
It didn't work either. The source code for ruptime
and ruptimed
are as follows:
// the functions used in the two program
// the connect_retry() function
#include "apue.h"
#include <sys/socket.h>
#define MAXSLEEP 128
int
connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
{
int nsec;
for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) {
if (connect(sockfd, addr, alen) == 0) {
return (0);
}
if (nsec <= MAXSLEEP/2)
sleep(nsec);
}
return (-1);
}
// the initserver function
#include "apue.h"
#include <errno.h>
#include <sys/socket.h>
int
initserver(int type, const struct sockaddr *addr, socklen_t alen,
int qlen)
{
int fd;
int err = 0;
if ((fd = socket(addr->sa_family, type, 0)) < 0)
return (-1);
if (bind(fd, addr, alen) < 0) {
err = errno;
goto errout;
}
if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
if (listen(fd, qlen) < 0) {
err = errno;
goto errout;
}
}
return(fd);
errout:
close(fd);
errno = err;
return (-1);
}
// the client end i.e. ruptime.c
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#define MAXADDRLEN 256
#define BUFLEN 128
extern int connect_retry(int, const struct sockaddr *, socklen_t);
void
print_uptime(int sockfd)
{
int n;
char buf[BUFLEN];
while ((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
write(STDOUT_FILENO, buf, n);
if (n < 0)
err_sys("recv error");
}
int
main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err;
if (argc !=2 )
err_quit("usage: ruptime hostname");
hint.ai_flags = 0;
hint.ai_family = 0;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if ((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
err_quit("getaddrinfo error: %s", gai_strerror(err));
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
if ((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0)
err = errno;
if (connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen) < 0) {
err = errno;
} else {
print_uptime(sockfd);
exit(0);
}
}
fprintf(stderr, "can't connect to %s: %s\n", argv[1],
strerror(err));
exit(1);
}
//the server end i.e. ruptimed is as follows:
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#define BUFLEN 128
#define QLEN 10
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
extern int initserver(int, struct sockaddr *, socklen_t, int);
void
serve(int sockfd)
{
int clfd;
FILE *fp;
char buf[BUFLEN];
for ( ; ; ) {
clfd = accept(sockfd, NULL, NULL);
if (clfd < 0) {
syslog(LOG_ERR, "ruptimed: accept error: %s",
strerror(errno));
exit(1);
}
if ((fp = popen("/usr/bin/uptime", "r")) == NULL) {
sprintf(buf, "error: %s\n", strerror(errno));
send(clfd, buf, strlen(buf), 0);
} else {
while (fgets(buf, BUFLEN, fp) != NULL)
send(clfd, buf, strlen(buf), 0);
pclose(fp);
}
close(clfd);
}
}
int
main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err, n;
char *host;
if (argc != 1)
err_quit("usage: ruptimed");
#ifdef _SC_HOST_NAME_MAX
n = sysconf(_SC_HOST_NAME_MAX);
if (n < 0)
#endif
n = HOST_NAME_MAX;
host = malloc(n);
if (host == NULL)
err_sys("malloc error");
if (gethostname(host, n) < 0)
err_sys("gethostname error");
daemonize("ruptimed");
hint.ai_flags = AI_CANONNAME;
hint.ai_family = 0;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if ((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {
syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s",
gai_strerror(err));
exit(1);
}
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr,
aip->ai_addrlen, QLEN)) >= 0)
serve(sockfd);
exit(0);
}
exit(1);
}
Is there anyone can help?
The header lines of the command netstat -nap:
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:4000 0.0.0.0:* LISTEN 2252/ruptimed
tcp 0 13033 192.168.1.221:47380 91.189.89.114:443 LAST_ACK -
tcp 74 0 192.168.1.221:43914 91.189.89.76:443 ESTABLISHED 1846/python
tcp 0 0 127.0.0.1:4000 127.0.0.1:56344 TIME_WAIT -
tcp 0 1 192.168.1.221:35442 192.168.1.89:24800 SYN_SENT 1478/synergyc
tcp 0 0 192.168.1.221:34957 74.125.71.125:5222 ESTABLISHED 1618/chrome
tcp 0 0 192.168.1.221:57795 203.208.46.163:443 ESTABLISHED 1618/chrome
tcp6 0 0 :::22 :::* LISTEN -
The 3th record is my service ruptime. Is it something wrong? I don't know much about its meanig.
I'm not a C expert but I know one thing: your daemon is probably binding to just one address/interface, and is accepting connections only from it.
Use netstat -nap
to check to which address the daemon is binding to.
If it is listening to 0.0.0.0
then it is accepting connections from every interface, including localhost.
If it is listening to 127.0.0.1
it will only accept from localhost.
I bet it's bound only to 127.0.0.1
, that's why you can't connect with any other address.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With